2012-10-05 5 views
7

この質問は、以下の2つのプログラムの1つとして、好奇心のポイントです。Perlスレッドでのガベージコレクション

Image :: Magickを使用していくつかの写真のサイズを変更しています。ちょっと時間を節約するために、私は自分のスレッドで各写真を処理し、セマフォを使って同時に動作するスレッドの数を制限します。もともと私はすべてのスレッドを一度に動かすことができましたが、スクリプトはすべての写真のために3.5GBを素早く割り当てることができました(私は2GBしか利用できません)。ディスクへのすべてのスワッピングのためにスクリプトは通常よりも5倍遅くなります。

use threads; 
use Thread::Semaphore; 
use Image::Magick; 

my $s = Thread::Semaphore->new(4); 
foreach (@photos) { 
    threads->create(\&launch_thread, $s); 
} 
foreach my $thr (reverse threads->list()) { 
    $thr->join(); 
} 

sub launch_thread { 
    my $s = shift; 
    $s->down(); 
    my $image = Image::Magick->new(); 

    # do memory-heavy work here 

    $s->up(); 
} 

これはすぐに500メガバイトを割り当て、これまで以上を必要とせずに非常にうまく動作します:

作業は、セマフォバージョンのコードは次のようになります。 (スレッドがポイントを作るために逆の順序で結合されている)

同時に80個のスレッドを起動し、それらのほとんどを遮断するからオーバーヘッドがあるかもしれません場合、私は疑問に思ったので、私はメインスレッドブロックするように私のスクリプトを変更:

my $s = Thread::Semaphore->new(4); 
foreach (@photos) { 
    $s->down(); 
    threads->create(\&launch_thread, $s); 
} 
foreach my $thr (threads->list()) { 
    $thr->join(); 
} 

sub launch_thread { 
    my $s = shift; 
    my $image = Image::Magick->new(); 

    # do memory-heavy work here 

    $s->up(); 
} 

このバージョンは正常に起動しますが、元のバージョンが使用していた3.5GBのスペースが徐々に累積されます。一度にすべてのスレッドを実行するよりも高速ですが、スレッドをブロックするよりもかなり遅いです。

私が最初に推測したのは、スレッドによって使用されるメモリはjoin()が呼び出されるまで解放されず、ブロックされるメインスレッドであるため、スレッドがすべて割り当てられるまで解放されません。しかし、最初の作業バージョンでは、スレッドはガードを多かれ少なかれランダムな順序で渡しますが、逆の順序で結合します。私の推測が正しければ、実行中の4つのスレッドよりも多くのスレッドがいつでもjoin()されるのを待つ必要があり、このバージョンも同様に遅くなるはずです。

なぜこれら2つのバージョンが異なるのですか?

答えて

3

4つ以上のスレッドを作成する必要はありません。 1つの大きな利点は、Perlインタプリタのコピーが76個少なくなることです。また、すべてのスレッドが多かれ少なかれ同じ時間に終了するので、刈り取り順序をむずかしくします。

use threads; 
use Thread::Queue qw(); 
use Image::Magick qw(); 

use constant NUM_WORKERS => 4; 

sub process { 
    my ($photo) = @_; 
    ... 
} 

{ 
    my $request_q = Thread::Queue->new(); 

    my @threads; 
    for (1..NUM_WORKERS) { 
     push @threads, async { 
      while (my $photo = $request_q->dequeue()) { 
      process($photo); 
      } 
     }; 
    } 

    $request_q->enqueue($_) for @photos; 
    $request_q->enqueue(undef) for 1..NUM_THREADS; 
    $_->join() for @threads; 
} 
+0

次にキューを試してみました。私はPerlで何が起こっているのか不思議で、セマフォの1つのバージョンを完全に動作させ、1つはひどく動作します。 – pconley

+0

あなたのバージョンでは、semをアンロックしなければならないスレッドだけが大量のメモリを使います。あなたが完了したときにそれらを刈り取るなら、4つのスレッドだけがいつでも大量のメモリを使用していることを意味します。最終的にそれらを刈り取るだけでは、最終的に80スレッドが大量のメモリを使います。 – ikegami

関連する問題