2008-04-16

Premature optimization is the root of all evil

かつてDonald Knuthは言った。"Premature optimization is the root of all evil."(下手な考え休むに似たり) と。

x264というH.264のエンコーダがあるが、これのマルチスレッドの実装は、1フレームごとにひとつ、スレッドを作るというものだ。しかも、pthreadだ。ここで私は思った。「Oh Please」と。「スレッドの作成と破棄は、高くつくぜ」と。スレッドプールで実装すれば、パフォーマンスがあがるのではないか、また、pthreadをWindowsでエミュレートするのは余計なオーバーヘッドがかかるのではないか、と思ったわけだ。

x264のソースコードを読んだ結果、pthreadを前提としたコードで、Windowsのスレッドには、容易に書き直せないことが分かった。しかも、スレッドプールにしなければならない。考えたあげく、ひらめいた。Windowsでpthreadを使いたい場合は、大抵はPthreads-w32を使うことになる。これは、有志が実装したpthreadだ。ならば、自分でも実装しない法はない。pthread_createで、実際にスレッドを作るのではなく、スレッドプールを使うのだ。

さて、Windows Vistaには、Thread PoolとConditinal Variableが実装されている。これらのAPIを使えば、やるべきことは、ただpthreadのラッパを用意することだけだ。プロセスを超えた処理はできないが、この場合、それは問題ではない。さっそくpthreadを実装しよう。

pthreadの実装は、実に楽であった。pthreadの仕様は、まあ悪くない。pthread_joinは、同じスレッドに対して複数回呼び出すことはできないという制限のおかげで、リソースリークもない。また、CRTで用意された専用のスレッド関数を使わなかった場合のリソースリークは、どうやらDLLを使うと、問題にならないらしい。完璧だ。

さっそく、pthread-win32と、私のpthreadの実装を比較してみた。結果は、残念なことに、パフォーマンスに差はなかった。さまざまなオプションで試したが、どうしてもエンコード速度は、誤差程度しか違わない。

そもそも考えてみると、x264の実装は、同時に実行されるスレッドは制限されている。スレッドの作成破棄のオーバーヘッドというのも、じつは私の環境ベンチマークした限りでは、0.1ミリ秒程度だ。対して、重い設定でエンコードしようとすると、秒間2~3フレームぐらいしかエンコードできない。1フレームの実行に500ミリ秒もかかっているわけだ。これはスレッド作成にかかる時間の実に五千倍に相当する。また、H.264は多重にフレームを参照できたりするので、あるスレッドが別のスレッドを待たなければならないことがよくある。などと色々理由は思いつくわけだ。それにしても、この結果は悔しい。

結局、出来上がったのは、64bitコードでも使える、限定的なpthreadのライブラリだった(pthread-win32は現在、64bitへのポータビリティに問題がある) x264で使っても、パフォーマンスは一切向上しない。やれやれだ。

余談だが、VistaのThread PoolとCondition Variableはとても使いやすい。また、今回pthreadの規格を深く読んだのだが、どうも納得のいかない点もある。これについてはまた今度。

No comments: