Ubuntu でジョブを並列実行する - Perl と Java の I/O 競合の違い

Ubuntu でジョブを並列実行する - Perl と Java の I/O 競合の違い

話題から外れている場合は申し訳ありませんが、これは、Ubuntu システム上で I/O 負荷の高い Perl/Java スクリプトを並列に実行する場合の相対的な効率に関するものです。

私はファイル コピー スクリプトの 2 つの簡単なバージョン (Perl と Java) を作成しました - 以下を参照してください。15 GB のファイルでスクリプトを実行すると、Ubuntu Server 12.04 を実行している 48 コア マシンでそれぞれ同様の時間がかかります (perl 2 分 10 秒、java 2 分 27 秒)。

ただし、6 つのインスタンスを並列で実行し、それぞれが異なる 15 GB の入力ファイルで操作すると、処理時間が大きく異なることがわかります。

  • Perl: 1 つのインスタンスが 2 分 6 秒で完了し、その他はすべて 27 分 26 秒から 28 分 10 秒かかります。
  • Java: すべてのインスタンスに 3 分 27 秒から 4 分 37 秒かかります。

topPerl プロセスが長時間実行されている間のプロセッサ コアを見ると、占有されているコアの I/O 待機率 (%wa) が 70% 以上であることがわかります。これは、何らかのディスク競合 (すべてのファイルが 1 つの HD 上にある) が発生していることを意味します。したがって、Java はBufferedReader、このディスク競合の影響をあまり受けないと考えられます。

質問 - これは妥当な結論のように思えますか? もしそうなら、この種のタスクで Perl スクリプトを Java と同じくらい効率的にするために、OS レベルまたは Perl で実行できるアクションを提案してもらえますか?

注: 私の目標は単にファイルをコピーすることではありません。実際のスクリプトには追加のロジックが含まれていますが、以下の簡略化されたスクリプトと同じパフォーマンス動作を示します。

パール

#!/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 のバッファリングの動作方法によるものと考えられます。この場合、Java で bufferedReader を使用したため、Java が有利になりました。Perl はディスクから約 4k をバッファリングします。

ここでいくつか試す方法があります。1つは、Perlのread関数を使用して一度に大きなブロックを取得することです。5月性能を上げる。

もう 1 つの選択肢としては、さまざまな mmap 関連の Perl モジュールを調査することが挙げられます。

答え2

本当の答えではありませんが、コメント内のコードは適切にフォーマットされていません。

GNU Parallel の場合、コピーにはこのバージョンを使用します。1 GB/秒/コアのオーダーで提供でき、並列でうまく動作します。

perl -e '$left=-s STDIN;
  while($read=sysread(STDIN,$buf,$left>131072?131072:$left)){
    $left-=$read;
    syswrite(STDOUT,$buf);
  }' < in > out

答え3

こんにちは。そうではないかもしれませんが、最初の観察から、Perl スクリプトは順次解釈される形式で実行されています。一方、Java プログラムはコンパイルされたプログラムとして実行されており、並列形式で実行されています。これが完了速度の違いの原因である可能性があります。

関連情報