2017-01-24 13 views
2

我想弄清楚如何正確實施ForkManager作爲我正在處理的一個項目的一部分,但遇到了FM似乎在產卵過程和做某些事情的情況,但需要永遠。ForkManager在調試模式下更快完成

但是,當我在調試代碼中嘗試FM時(通過將最大進程設置爲0),代碼將在合理的預期時間範圍內完成。

這裏是我遇到的麻煩的代碼...

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; 

所以,如果我設置$cpu_count爲0,過程沒有問題完成細末,用原來的C代碼完全在一兩分鐘(只有sub foo {return rand;}〜2秒),但是當FM打開時,它似乎會持續很長時間。它看起來好像是在運行,但是當我放置打印語句如print "at rows $i and $j"來診斷問題時。

運行時是相同的,如果我拿出所有FM相關的代碼,只是試圖有規則的雙重foreach循環。

在此先感謝!

回答

3

那是因爲從孩​​子送到父母數據寫入到磁盤(請參見檢索數據結構中Parallel::ForkManager):

在給定的子進程中引用的數據結構 被序列化並寫入到一個文件通過 可存儲。該文件隨後被讀回到存儲器中,並創建屬於父進程的新數據結構 。請考慮其可能暗示的性能 ,因此請儘量減少返回的結構。

在調試模式下,不會發生分叉,因此可以直接傳遞結構而不必保存和加載。

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);。不需要創建一個新的數組。