並列処理レベルの実用的な分類:TLP(Thread-Level Parallelism)

項目 説明
インターフェース カーネルスレッドライブラリAPI(Pthread、Win32 thread、JAVA threadクラス、OpenMPなど)
処理対象 実行中のプロセス空間内のメモリ上の全てのデータ
処理対象データ量 一つのプロセス空間がアクセス可能な上限のメモリ、数〜数十GB(メモリ)とI/O
実行主体 一台のコンピュータ上のSMP構成の全てのプロセッサ
同期手法 カーネルスレッドライブラリが提供する機能とその組み合わせ(ミューテックスセマフォ、条件変数、イベントなど))
同期間隔 1〜数百ms(10msが目安)
期待できる性能向上率範囲 数倍〜数十倍((一台のコンピュータ上で実現できるプロセッサ数とメモリ、ストレージ(とストレージI/O)の性能によって制限される) )
制限要素 一つのプロセス空間から利用できるハードウェアリソースしか利用できない

TLPは難しいとよく言われるのですが、どの辺が難しいのかを考えてみました。
TLPが並列処理の一種であり、並列処理のプログラムが想定した通りに動作するために必要な要件を揃えればその通りに動くというのはTLPにおいても同じです。でもよく考えるとILPやPLPでは気にしなくていいことをTLPでは気にする必要がいくつかあることに気付きました。

  • 「データの更新(メモリの読み書き)の実行順序を気にする必要がある」

というのは当たり前のことですが、ILPやTLPではさほど意識しなくてもいい状況があります。 ILPの並列計算においては最終的にはコーディングした順序にシリアルに実行される(されたのと論理的に同じ状態になる)ことをハードウェアが保証しています。したがって同期を考慮する必要はありません。PLPの通信においては明確に意図して実行しなければ通信は不可能です。つまり他のプロセスの情報を不意に破壊してしまうということはありません。並行プログラミング上の誤りは「通信の失敗」という形で表れます*1

  • 「どのスレッドがいつ実行されるかわからない」

これは並行プログラミングの本質なのでそれで問題がないようにする必要が元々あるのですが、Windowsだったりすると秒単位でCPU時間の割り当てが来ないことがあったりする(もしくは自らそういう状況を自分で作り出せてしまう)というのはあまり自然に意識することではないのではないでしょうか。TLPはAPIリファレンスだけをみると無制限に何でもできる、性能による制限はないように見えるのですが、実際にはコンテキストスイッチの時間当たりの切り替え回数、他のスレッドが消費するCPU時間などの性能的な要素によって動作が制限されます。
GUIのプログラムなど人間に対するUIを提供するプログラムにおいては人間の入力から出力までの処理にかかる時間を小さくする努力をし、処理が停滞する原因になるロックの粒度を小さくする((複数のスレッド同士が接触する部分という意味でサーフェイスを小さくする、という言い方もします))ということを意識した方がいいでしょう。

*1:互いに書き込む領域をプロセス共有メモリなどで作成すればTLPに似た状況を一部作れますが、TLPのように無制限ではありません