Извините, если это не по теме. Речь идет об относительной эффективности параллельного выполнения скриптов Perl/Java с большим объемом ввода-вывода в системе Ubuntu.
Я написал две простые версии скрипта копирования файлов (Perl и Java) - см. ниже. Когда я запускаю скрипты на файле размером 15 ГБ, каждый из них занимает одинаковое количество времени на 48-ядерной машине с Ubuntu Server 12.04 (perl 2m10s, java 2m27s).
Однако когда я запускаю шесть экземпляров параллельно, каждый из которых работает с отдельным входным файлом размером 15 ГБ, я наблюдаю совершенно разное время обработки:
- Perl: один экземпляр завершается за 2 мин 6 с, все остальные — за 27 мин 26 с — 28 мин 10 с.
- Java: все экземпляры занимают 3 мин 27 с - 4 мин 37 с.
Глядя на ядра процессора во top
время длительных процессов Perl, я вижу, что занятые ядра имеют процент ожидания ввода-вывода (%wa) 70%+, что подразумевает некую конкуренцию за диск (все файлы находятся на одном жестком диске). Тогда, по-видимому, Java BufferedReader
каким-то образом менее чувствительна к этой конкуренции за диск.
Вопрос - Кажется ли это разумным выводом? И если да, может ли кто-нибудь предложить какие-либо действия, которые я могу предпринять на уровне ОС или в Perl, чтобы сделать скрипт Perl таким же эффективным, как Java для такого рода задач?
Обратите внимание: моя цель — не просто копирование файлов. Мои настоящие скрипты содержат дополнительную логику, но демонстрируют такое же поведение производительности, как и упрощенные скрипты, представленные ниже.
Перл
#!/usr/bin/perl -w
open(IN, $ARGV[0]) || die();
open(OUT, ">$ARGV[1]") || die();
while (<IN>) {
print OUT $_
}
close(OUT);
close(IN);
Джава
import java.io.*;
public class CopyFileLineByLine {
public static void main(String[] args) throws IOException {
BufferedReader br = null;
PrintWriter pw = null;
try {
br = new BufferedReader(new FileReader(new File(args[0])));
pw = new PrintWriter(new File(args[1]));
String line;
while ((line = br.readLine()) != null) {
pw.println(line);
}
}
finally {
if (pw != null) pw.close();
if (br != null) br.close();
}
}
}
решение1
Разница в производительности, скорее всего, заключается в том, как работает буферизация между Perl и Java. В этом случае вы использовали A bufferedReader в java, что дает ему преимущество. Perl буферизует около 4k с диска.
Вы можете попробовать несколько вещей здесь. Одна из них — использовать функцию чтения в Perl, чтобы получать большие блоки за раз. Этоможетповысить производительность.
Другим вариантом может быть исследование различных модулей Perl, связанных с mmap.
решение2
Не совсем ответ, но код в комментариях отформатирован не очень хорошо.
Для GNU Parallel я использую версию этого для копирования. Она может доставлять в порядке 1 ГБ/с/ядро и хорошо работает в параллельном режиме:
perl -e '$left=-s STDIN;
while($read=sysread(STDIN,$buf,$left>131072?131072:$left)){
$left-=$read;
syswrite(STDOUT,$buf);
}' < in > out
решение3
Привет. Это может быть не так, но с первого взгляда ваш скрипт perl выполняется в последовательной интерпретируемой манере. В то время как ваша программа java выполняется как скомпилированная программа и делает это в параллельной манере. Это может объяснить разницу в скорости завершения.