2017-01-24 3 views
2

私が取り組んでいるプロジェクトの一部に対してForkManagerを適切に実装する方法を理解しようとしていますが、FMがプロセスの作成や処理を行っているようですが、永遠に。ForkManagerはデバッグモードで素早く終了します

しかし、デバッグコードでFMを試してみると(最大プロセスを0に設定すると)、コードは妥当で予想された時間枠内で完了します。ここで

私はとのトラブルを抱えていたコード...だから私は0に $cpu_countを設定している場合

use strict; 
use warnings; 
use Parallel::ForkManager; 

sub read_table { 
    # takes a filename and reads in a CSV file. 
    # works fine and thus is omitted here 
} 
sub foo { 
    # originally an Inline::C subroutine 
    # for purpose of debugging replaced with randgen 
    return rand; 
} 

my $cpu_count = 0; my $epsilon = 1e-16; 
my @tt = read_table('tt.csv'); 
my @tc = read_table('tc.csv'); 
my @nm = ($epsilon) x scalar @tc; 
my @results; 
my $pm = new Parallel::ForkManager($cpu_count); 
$pm->run_on_finish(sub{ 
    my $i = $_[1]; my $m = $_[5]; 
    $results[$i] = $m; 
}); 
foreach my $i (0..$#tt) { 
    $pm->start and next; 
    my @r; 
    if (scalar @{$tt[$i]} > 1) { 
    foreach my $j (0..$#tc) { 
     if (scalar @{$tc[$j]} > 1) { 
      push @r, foo(scalar @{$tt[$i]}, scalar @{$tc[$j]}, \@{$tt[$i]}, \@{$tc[$j]}); 
      } else { 
       push @r, $epsilon; 
      } 
     } 
    } else { 
     @r = @nm; 
    } 
    $pm->finish($i, [@r]); 
    undef @r; 
} 
$pm->wait_all_children; 

あり、このプロセスは数分で完了し、元のCコードで、問題なく細かい完了します(sub foo {return rand;}は〜2秒しかない)が、FMがオンになっていると、長い時間続くように見える。しかし、私がprint "at rows $i and $j"のような印刷文を入れて問題を診断すると、実行中のように見えました。

すべてのFM関連コードを取り出して、代わりに通常の二重foreachループを使用しようとすると、ランタイムは同じでした。

ありがとうございます!

子から親に送信されたデータがディスクに書き込まれているため

答えて

3

は(Parallel::ForkManagerにデータ構造を取得を参照してください) :

与えられた子プロセスで参照されるデータ構造 がシリアル化され、ファイルに書き出しますby 保存可能です。その後、ファイルがメモリに読み込まれ、親プロセスに属する新しいデータ構造 が作成されます。パフォーマンスを考慮してください ペナルティ、それは意味することができますので、返された構造体を小さくしてください。

デバッグモードではフォークが発生しないため、構造体を保存してロードせずに直接渡すことができます。

Thread::Queueより良い結果が得られるかもしれません。あなたの労働者が親に戻ってデータを転送し、より大幅にプロセスを作成してのオーバーヘッドが実際の作業負荷よりも大きくなるように少しないので

#!/usr/bin/perl 
use strict; 
use warnings; 

use threads; 
use Thread::Queue; 

sub read_table { 
    map [ map rand, 1 .. 100 ], 1 .. 100; 
} 
sub foo { 
    [ @_ ] 
} 

my $cpu_count = 20; my $epsilon = 1e-16; 
my @tt = read_table('tt.csv'); 
my @tc = read_table('tc.csv'); 
my @nm = ($epsilon) x scalar @tc; 
my @results; 

my ($q_in, $q_out) = map 'Thread::Queue'->new, 1, 2; 
my @workers = map threads->create(sub{ 
    while(defined(my $i = $q_in->dequeue)) { 
     warn $i; 
     my @r; 
     if (scalar @{$tt[$i]} > 1) { 
      for my $j (0 .. $#tc) { 
       if (scalar @{$tc[$j]} > 1) { 
        push @r, foo(scalar @{$tt[$i]}, scalar @{$tc[$j]}, \@{$tt[$i]}, \@{$tc[$j]}); 
       } else { 
        push @r, $epsilon; 
       } 
      } 
     } else { 
      @r = @nm; 
     } 
     $q_out->enqueue([$i, @r]); 
    } 
}), 1 .. $cpu_count; 

$q_in->enqueue(0 .. $#tt); 
$q_in->end; 


for (0 .. $#tt) { 
    my $r = $q_out->dequeue; 
    my $i = shift @$r; 
    warn "$i: $r->[2][2][1]"; 
} 
$_->join for @workers; 
0

それはです。

提案:

  • 代わりの要素のグループを割り当てる、子供に@ttの単一要素を割り当てます。
  • 親の中で@{$tt[$i]}が空の場合のケースを処理します。

chorobaのソリューションはオーバーヘッドを削減しますが、元のプログラムの非効率性を維持しました。彼らのソリューションは、私の提案を実装することによって、はるかに高速になる可能性があります。ところで


$pm->finish($i, [@r]);$pm->finish($i, \@r);として書かれた方が良いです。新しい配列を作成する必要はありません。

関連する問題