Haskell による並列・並行プログラミング #17 15章 デバッグ、チューニング、外部コードとのインターフェース
担当: lotz さん
ThreadStatus をつかうとスレッドの状態を確認できる
デバッグには使えるけどアプリケーションに使うのは禁止すべき
ThreadScopeは番号でスレッドを管理している
スレッドにラベルをつけられる
スレッドのイベントログをとれる
-threaded -eventlog でコンパイル
+RTS -l で実行
ghc-events で eventlog をみる
デッドロックの検出
GHCではスレッドとMVarはヒープ上にある
MVarがどのスレッドから参照されているかという記録がある
あるMVarによってブロックされているリストをヒープオブジェクトとして持っている
オブジェクトルートから到達できないスレッドはデッドロックしている
デッドロックが検出されるとすべてのスレッドに例外が送られる
分岐によってデッドロックするしないが決まる場合がある
コンパイラ最適化によって自明な分岐が取り除かれてデッドロックが起こる場合がある
並行・並列プログラムのチューニング
早すぎる最適化をするな、しかし早すぎる最適化の回避を理由にひどいコードを書くな
実行時間を測定して、一番重い処理に集中せよ
スレッドは軽量
1,000,000個のスレッドを生成しても 0.7 秒、71MBくらいの消費量
並行データ構造の共有
MVar: 公平性を保証しようとするので、競合が激しいと性能が出ない
TVar: 性能上の落とし穴はあるが、競合のある状況下ではMVarより高性能
IORef: atomicModifyIORef とともに使えば性能上良い結果を得られる
RTS オプション
-N コア数
-qa 実行時のスレッドを特定のコアに固定する
-A コアごとに確保するメモリ領域のサイズを制御する
L2 キャッシュくらいのサイズが良い
-l デッドロック検出のためのGC稼働までの時間
-C コンテキストスイッチの間隔
FFI
外部呼び出しはHaskellと並行に走る
Cの関数を並行に動かすにはOSスレッドを使うことになる
GUIライブラリの多くは1つのOSスレッドだけにしかAPIを使うことを許さない
OpenGLはAPI呼び出しの間で状態をスレッドローカルな格納場所に保存する
外部呼び出しが常に結び付けられたOSスレッドで実行されることを保証する束縛スレッド
forkOS
FFI 中の非同期例外
Haskell スレッドが外部呼び出しをすると、非同期例外を受け取れなくなる
この制限を回避するコツはFFIを別スレッドで実行すること
外部からの呼び出し(foreign export)
それぞれの呼び出しは新しい束縛スレッドになる
スレッド内からの外部呼び出しは同じOSスレッドで実行される
GUIのコールバックを扱うために重要