Параллельное выполнение заданий в Ubuntu — различия в конфликтах ввода-вывода между Perl и Java

Параллельное выполнение заданий в Ubuntu — различия в конфликтах ввода-вывода между Perl и Java

Извините, если это не по теме. Речь идет об относительной эффективности параллельного выполнения скриптов 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 выполняется как скомпилированная программа и делает это в параллельной манере. Это может объяснить разницу в скорости завершения.

Связанный контент