8

以下の 2 つのプログラムのいずれかが機能するため、この質問は興味深い点です。

Image::Magick を使用して、多数の写真のサイズを変更しています。少し時間を節約するために、各写真をそれぞれのスレッドで処理し、セマフォを使用して同時に動作するスレッドの数を制限します。最初はすべてのスレッドを一度に実行できるようにしていましたが、スクリプトはすぐにすべての写真に 3.5 GB を割り当て (2 GB しか使用できませんでした)、ディスクへのすべてのスワッピングのために、スクリプトの実行速度は通常の 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();
}

これにより、500MB がすぐに割り当てられ、それ以上必要とせずに非常にうまく動作します。(ポイントを作るために、スレッドは逆の順序で結合されています。)

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.5 GB のスペースが徐々に蓄積されます。一度にすべてのスレッドを実行するよりも高速ですが、スレッドをブロックするよりもかなり遅くなります。

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

では、なぜこれら 2 つのバージョンはそれほど異なるのでしょうか。

4

1 に答える 1

4

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;
}
于 2012-10-05T20:04:29.433 に答える