Intel Threading Building Blocksと実行粒度

以前の日記Intel Threading Building Blocks(以下TBB)について書いたが、その実行形態についてちょっと考えてみた。
TBBでは、(ソースを追いかけてみると)ループの並列化なんかも最終的にはタスクに落ちる様子。TBBにおける「タスク」とは、特定のクラス("class task")を継承したクラスのインスタンスで、それを"spawn()"という関数に渡すと、タスククラス中の"execute()"というメソッドを並列に実行してくれる、という形になっている。
なんか、リファレンスカウントの設定とか、インスタンスをnewするときに、メモリアロケータを指定していたりするけど、本質的にはそれだけ。

spawn()は適当なデータ構造(ツリー?)にタスクを登録する。で、スレッドがそこからタスクを取り出しては実行する("execute()"を呼び出す)という形態なんだろう。

で、これの何が嬉しいかだが、やはりタスクのオーバヘッドが小さいことだろう。タスクといいながら、実体はただのクラスのインスタンスなので。
タスクのオーバヘッドが小さいと、実行粒度を小さくすることができる。その結果、比較的小さい処理でも並列に実行して、速度を稼ぐことができる。
…ということはあんまり主張していないのかな?チュートリアルでは、それより、負荷分散がうまく行くことを利点にあげている。

しかし、こういう実行形態だと、このタスクがブロックすると、まずいことになる。スレッドは、

  • ツリー?からタスクを取り出す
  • execute()を呼び出す。
  • 終了したら次のタスクを取り出す
  • 以下繰り返し

という処理を行っているのだとすると、"execute()"を呼び出す所でブロックしてしまうと、ツリーに他に実行可能なタスクがあってもそのスレッドは動作を停止してしまうからである。

チュートリアルにもこう書いてある:

The task scheduler is intended for high-performance algorithms composed from non-blocking tasks. It still works if the tasks rarely block. However, if threads block frequently, there is a performance loss when using the task scheduler because while the thread is blocked, it is not working on any tasks.

TBBはfuture(PARDSで言うSync)をサポートしていないけれど、これも原因なのかも知れない。(futureを使うと、taskが途中でブロックするプログラムを書きがちになるし。)

PARDSでも(簡単に)TBBと同じような形式のタスクをサポートすることはできるけど、あんまり気がすすまないなぁ。forkを使ったアドレス空間の分離も使えなくなるし。プログラマの負担になるだけだよなぁ…