2

並列化したい perl スクリプトがあります。

これは、3400 行whileの別のループ内に 11000 行を超えるループで構成されwhileているため、非常に遅くなります。

open (FILE1, "File1.txt") or die "Can't open File1";
open (OUT, ">Outfile.txt");

while (<FILE1>)
{ 
 my @data=split (/ /, $_);
 my $RS=1;
 open (FILE2, "File2.txt") or die "Can't open File2";
 while (<FILE2>)
 { 
  my @value=split (/ /, $_);
  if ($data[$RS] == 1) {print OUT $value[1];$RS++;}
  elsif ($data[$RS] == 2) {print OUT $value[2];$RS++;}
  elsif ($data[$RS] == 0) {print OUT $value[3];$RS++;}
 }
 close FILE2;

}

File13440 ジョブを送信できるように、すべての行で qsub と同等の処理を行う方法を探しています。助言がありますか?できればperlを使い続けたいです。このコードを bash スクリプト内に挿入しようとしましたが、言語を別のスクリプト内に挿入する方法がよくわかりません。

MyFile1には、列に情報を含む ID のリストが含まれています。各列は、 の 1 つの行に関連付けられFile2ます。次々にではなく、複数の ID に対して 2 番目のループを同時に実行できるようにしたいと考えています。

File1
ID     RS_10    RS_15    RS_30
23     1        0        1
34     2        2        0
45     1        1        0
23     0        0        2
10     2        1        1


File2
RS_10 A B C
RS_15 D E F
RS_30 G H I
4

1 に答える 1

5

最適化の第 1 のルールは、早すぎないことです (つまり、コードをプロファイリングせずに時期尚早の結論に飛びつくことです)。

2 番目のルールは、おそらくキャッシングに関するものです。

あなたのFile2はあまり大きくありません。それをメモリにロードすると思います。これには次の利点があります。

  • 解析は一度だけ行います。
  • ファイルはそれほど大きくないので、スペースはそれほど問題になりません。
  • 検索を非常に簡単にするデータ構造を作成できます。

その最初のポイントについて: 各行を 3,000 回以上分割します。それらのサイクルは、よりよく費やされた可能性があります。

その 3 番目のポイントについて: インデックス変換を行っているようです:

1 → 1, 2 → 2, 0 → 3

if/elsif スイッチ (線形の複雑さ) を使用してすべての値をテストする代わりに、この変換 (一定時間のルックアップ) を行う配列を使用できます。

my @conversion = (3, 1, 2);
...;
print OUT $value[$conversion[$data[$RS++]]];

このインデックス変換が一定である場合、解析時に一度だけ行うことができますFile2。これは次のようになります

use strict; use warnings;
use autodie; # automatic error handling

my @file2;
{
  open my $file2, "<", "File2.txt";
  while (<$file2>) {
    my (undef, @vals) = split;

    # do the reordering. This is equivalent to @vals = @vals[2, 0, 1];
    unshift @vals, pop @vals;

    push @file2, \@vals;
  }
}

これで、 の繰り返しに進むことができますFile1File2今から対応するエントリを印刷すると、次のようになります

open my $file1, "<", "File1.txt";
<$file1>; # remove header
while (<$file1>) {
  my ($id, @indices) = split;
  print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;
  # but I guess you'd want some separator in between
  # If so, set the $, variable
}

このアルゴリズムは依然として 2 次 (変装しmapた - ループにすぎません) ですが、これにはより優れた定数係数が必要です。for入力例を指定した上記のコードの出力は次のとおりです。

23 A F G
34 B E I
45 A D I
23 C F H
10 B D G

(付き$, = " "; $\ = "\n")。

ここからどこへ

この最後のステップ (File1 のループ処理)は並列化できますが、あまり役に立たない可能性があります。IO は遅く、スレッド間の通信は高価であり (IPC はさらに高価です)、出力はランダムな順序になります。多数のワーカーを生成し、未解析の行をキューに渡すことができます。

use threads; # should be 1st module to be loaded
use Thread::Queue;
use constant NUM_THREADS => 4; # number of cores

# parse the File2 data here

my $queue = Thread::Queue->new;
my @threads = map threads->new(\&worker), 1 .. NUM_THREADS;

# enqueue data
$queue->enqueue($_) while <$file1>;
# end the queue
$queue->enqueue((undef) x NUM_THREADS); # $queue->end in never versions

# wait for threads to complete
$_->join for @threads;

sub worker {
  while(defined(my $_ = $queue->dequeue)) {
    my ($id, @indices) = split;
    print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;
  }
}

@file2これにより、がすべてのスレッドにコピーされることに注意してください。興味深い事実: サンプル データの場合、このスレッド化されたソリューションには約 4 倍の時間がかかります。これは主にスレッド作成のオーバーヘッドであるため、データにとってはそれほど問題になりません。

いずれにせよ、コードをプロファイリングして最も効果的に最適化できる場所を確認してください。私は優秀をお勧めしDevel::NYTProfます。たとえば、この非常に限られたデータを使用したスレッド化されていないテストの実行ではautodie、実際の処理を行うよりも多くの時間を費やした友人が暗示するオーバーヘッドがありました。あなたにとって、最も高価な回線はおそらく

  print $id, map $file2[$_][$indices[$_]], 0 .. $#indices;

しかし、Perl 内部でできることはあまりありません。

于 2013-07-24T19:29:25.063 に答える