できる!並列・並行プログラミング

27,817 views
27,490 views

Published on

現在のマルチスレッドプログラミングの抱える問題点と、代替案をわかりやすく解説いたします。最近登場したConcurrent Revisionsも解説します。

Published in: Technology
1 Comment
96 Likes
Statistics
Notes
No Downloads
Views
Total views
27,817
On SlideShare
0
From Embeds
0
Number of Embeds
6,303
Actions
Shares
0
Downloads
240
Comments
1
Likes
96
Embeds 0
No embeds

No notes for slide

できる!並列・並行プログラミング

  1. 1. できる!並列・並行プログラミング 田中英行 <tanaka.hideyuki@gmail.com> Tech Talk@PFI 2011/10/20
  2. 2. “The Free Lunch is Over!”21世紀初頭、人類は岐路に立たされていた 永遠に続くかと思われたプロセッサ 高速化に陰りが見え始め プロセスの微細化は物理的限界を 迎えようとしていた プロセッサメーカはマルチコアへと走り プログラマに困難を押し付ける 上がらないクロック 下がるIPC Out-of-OrderからIn-Orderに フリーランチを取り戻せ 果報は寝て待つ時代を再び!
  3. 3. 本日の内容• ぼーっと寝ているだけで、プログラムが 速くなる時代は終わってしまった• どげんかせんといかん!
  4. 4. 並列・並行の違い• 並列(Parallel) – タスクを高速化するために、プログラムを複 数のプロセッサで動作させる – 目的は高速化(速くならないのなら意味は無 い)• 並行(Concurrent) – 複数のタスクを同時に実行する – タスクを同時に実行すること自体が目的 ※よく誤解されますが、インプリによる分類ではないです
  5. 5. 分散• 物理的に独立した、複数のノードで 並列・並行を行う• 今回は扱いません
  6. 6. マルチスレッドプログラミング• 1ノード内での並列・並行プログラムに 用いる• 並列・並行はスレッドだけじゃない – OpenMP(プラグマ) – OpenCL/CUDA(DSL) – Implicit Parallelism(Parallel Haskell) – (Nested)Data Parallelism(DPH) – Automatic-Paralellisation(お餅) – これらの話は今回はしません
  7. 7. スレッドとは• ここでは次のようなものと想定 – メモリ空間を共有 – 固有のスタックを持つ – プリエンプティブ• 実装はいろいろ – OSスレッド(pthreadなど) – グリーンスレッド(処理系固有のスレッド) – 軽量スレッド(Erlang/Haskellなど)
  8. 8. マルチスレッドプログラミングは 難しい スレッド
  9. 9. なんで難しいのか?• 複数のスレッドはやり取りする必要がある – そうでなければ、ただのタスクの集合• 複数のスレッドが同一のリソースにアクセ スするとき、非決定状態が発生する
  10. 10. 簡単な例• 次のプログラムにはバグがあります。 そのバグを指摘しなさい(1分で10級)
  11. 11. Race Condition(競合状態)• 複数のスレッドが同一のリソースにアク セスする際、予期しない状態になること がある• そのような状態をRace Conditionという• マルチスレッドプログラムは一般的に非 決定な計算になるので、この手のバグの 再現は困難を極めることが多い
  12. 12. Race Conditionの例
  13. 13. 並行性制御・排他制御• ロック – あるリソースに同時にアクセスするスレッドが一 つであれば、Race Conditionは発生しない – リソースへのアクセス権を制御する仕組み – mutexへのロック・アンロックで実現• 排他制御 – 同一リソースに複数スレッドがアクセスしないよ うにすること – 排他制御が必要なコード上の箇所を 「クリティカルセクション」という
  14. 14. さっきの解答• cnt_ にロックによる排他制御を追加する
  15. 15. そう彼は言葉巧みに私に近づいて来ました 手を変え品を変え 言語組み込みなら大丈夫 Boostなら大丈夫 ・・・ でも、待っていたのは デバッグ地獄だったんです
  16. 16. 第二章Locks Considered Harmful
  17. 17. ロックは害悪である• ロックを用いたすべてのプログラムは、 バグがあるか、もしくはまだバグが見つ かっていないかのどちらかである “Java Concurrency in Practice”• ロックによる並行プログラミングは、 アセンブリでプログラムするに等しい “出典不明”• トイレロックが取れない “P社社員”
  18. 18. ロックが難しいと見抜けないと (ロックを扱うのは)難しい• ロックのもたらすバグ・不具合一覧 – アンロックし忘れ – デッドロック – ロックの不足(Race Condition) – ロックの過剰(全然並列にならない) – モジュラリティの欠如
  19. 19. (1)アンロックし忘れ• ロックしたmutexをアンロックし忘れる• リソースに永久にアクセスできなくなる• 例外安全性とも関係• 解決法: – 常にRAIIを用い、手動でのロック・アンロッ クは“決して行なってはいけない”
  20. 20. (2)デッドロック• 複数のロックを複数のスレッドが同時に 獲得しようとする際に発生• スレッドが永久に停止• 解決法: – 必ず同じ順番でロックを取るようにする – ロックに順序を付け、これから獲得しようと するロックを予め全て列挙、その順番でロッ クしなければいけない
  21. 21. (3)ロックの不足• クリティカルセクションでロックを忘れ る• 解決法: – ありません – 気をつけるしかない \(^o^)/
  22. 22. (4)ロックの過剰• 必要のないところでロックを取る – 粒度の大きなロック • ハッシュテーブルで全要素をロックするなど – 粒度の小さなロックを記述するのは、非常に 難しい• 解決法: – 本質的に困難 • 特定のほげほげをfine grainedに実装しましただけ で国際学会レベル
  23. 23. (5)モジュラリティの欠如• ロックを用いたプログラミングは互いに 組み合わせることができない• 解決法: – 原理的に不可能\(^o^)/ \(^w^)/ \(^t^)/
  24. 24. ロックに潜む本質的欠陥• ロックを用いたルーチンは、Composable でない – 互いに組み合わせることができない – 例で説明します
  25. 25. 例:銀行システム• ストレートな実装
  26. 26. 問題点• 例:振込関数を書く – 振込処理はトランザクション – ACI(D)が必要• これはだめ – 中間状態が見える
  27. 27. 振込:修正• あらかじめ両方のロックをとっておく – ロック変数を暴露、カプセル化の崩壊• しかし、これで良いのか?
  28. 28. デッドロック• デッドロックの可能性 – ロックの順番を同じにしなければならない
  29. 29. モジュラリティの低下 ロック粒度の低下• すべての操作が、ロックする可能性のあ るmutexを列挙しなければならない• これらすべてを一度にロックしなければ ならないので、一部不要なものがあった としてもロックを回避できない – ロックの獲得の順番は必ず守らなければなら ないので
  30. 30. 組み合わせ• 例題:銀行Aから銀行Cに振込を行う。た だし、銀行Aに十分な残高がなかったら、 銀行Bから銀行Cに振込を試みる。
  31. 31. 解答例:まちがい• 予めロックを全部取る(順番は今は不問) – aからの振込に成功した場合、 bのロックを行なってはいけない
  32. 32. 解答例:まちがい(2)• 当然これはバグ
  33. 33. Q:どうすればいいのか?• A:どうにもできない – 状況に応じて必要なロックが変動する場合、 それらを安全にロックすることは不可能• これはすなわち – ロックを用いたプログラムにモジュラリティ は存在しない – ロックを用いたプログラムを組み合わせるこ とは不可能である
  34. 34. 第三章Lock Must Go!
  35. 35. マルチスレッドプログラミングに 銀の弾丸なし• すべてを解決する夢の様なものはない – お餅うにょーん• 実装するものに対して適切なものを選ぶ 必要がある – 方法はいろいろある – 一長一短
  36. 36. でも、ロックだけは絶対に死んでもイヤ
  37. 37. ロックの代替物• メッセージパッシング• Software Transactional Memory(STM)• Concurrent Revisions – 後で説明
  38. 38. メッセージパッシング• スレッドがそれぞれメッセージキューを 持ち、スレッド間の通信はメッセージの やり取りに限る – そもそもリソースを共有しないので、Raceが 起きない• プログラミングモデルに制限がかかる
  39. 39. STM• メモリ操作をAtomicity、Consistency、Isolation を満たすトランザクションとして記述する – 各トランザクションは、シーケンシャルに実行さ れた時と同一の結果にならなければならない• 実装方式はいろいろ – 楽観的並行性制御にて実行、競合したらロール バックするのが一般的 – 取り消し不可能な操作をトランザクションから排 除する必要がある
  40. 40. これらのものをサポートする 処理系• Erlang – immutable – メッセージパッシング(ScalaとかHaskellも)• Closure – 共有資源が次のいずれか • Transactional Variable • Thread Local• Haskell – STM(型レベルでロールバックの正しさを保証)
  41. 41. 第四章Concurrent Revisions
  42. 42. Concurrent Revisions• 最近提唱された並行性制御のための手法 – ロック無し – Waitなし – ロールバックなし – ロールバックしないので取り消し不可能な操 作を行なっても良い
  43. 43. 参考文献• Sebastian Burckhardt, Alexandro Baldassion, and Daan Leijen, Concurrent Programming with Revisions and Isolation Types, in Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications (OOPSLA10), ACM SIGPLAN, Reno, NV, October 2010 – http://research.microsoft.com/en- us/projects/revisions/
  44. 44. 概要• バージョン管理のアナロジーで 共有リソースを扱う
  45. 45. コンポーネント• versioned<T> – バージョン変数• revision – リビジョンをあらわす変数• fork – 並行に実行、子は別リビジョンに• join – リビジョンをマージする
  46. 46. 楽観的並行性制御• versioned変数はスレッドごとに領域が割 り当てられる – Raceが発生しない• Joinの際に決定的(Deterministic)に コンフリクトを解消 – デフォルトでは、子の変更を優先
  47. 47. Deterministic• Deterministic Concurrency – 非決定性がない
  48. 48. cumulative変数• コンフリクト解消の方法をカスタマイズ – λorig main child -> result
  49. 49. リビジョンツリーの形に制約• 直接の子供(もしくはそれをjoinしたリビ ジョン)しかjoinできない – 正しいコンフリクト解消のため
  50. 50. Concurrent Revisions:Pros• Race Conditionが発生しない• 決定的であるので、再現性の低いバグに 悩まされる必要がない – 本質的に決定的である必要がある並列には 特に向いている• (Concurrent Revisionsの)仕組みが単純 実装が容易 – STMは難しい
  51. 51. Concurrent Revisions:Cons• 不正なjoin – OriginalのC#への実装はランタイムエラー – Haskellへの実装ではRank2Polymorphismを 用いて型エラーにすることが可能• Deterministicにコンフリクト解消ができる 必要がある – そうでないものには向かない
  52. 52. 性能• オーバーヘッド – 非常に軽微
  53. 53. 性能• ゲームの並列化 – 4コアマシンでのFPS
  54. 54. 性能その他
  55. 55. You can try it NOW!$ git clone git://github.com/tanakh/concurrent_revisions.git$ cd concurrent_revisions.git$ ./waf configure$ ./waf build$ sudo ./waf install Fork it!
  56. 56. デモ
  57. 57. まとめ• 並行性制御に銀の弾丸なし• でも、ロックはもう使ってはいけない• ロックの代替を状況に応じて使い分け – メッセージパッシング – STM – Concurrent Revisions

×