1 / 33

並列プログラミングの Practice 課題を解いてもらうための布教活動

並列プログラミングの Practice 課題を解いてもらうための布教活動. 田浦. 並列問題解決のステップ. decomposition ( 並列性抽出): 問題のタスクへの分割 mapping ( 負荷分散): タスクを API レベルのスレッド/プロセスにマッピング coordination: タスク間の通信,依存関係を API または instruction レベルの通信・同期に翻訳する. 最も簡単な例.

loan
Download Presentation

並列プログラミングの Practice 課題を解いてもらうための布教活動

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 並列プログラミングのPractice課題を解いてもらうための布教活動並列プログラミングのPractice課題を解いてもらうための布教活動 田浦

  2. 並列問題解決のステップ • decomposition (並列性抽出): • 問題のタスクへの分割 • mapping (負荷分散): • タスクをAPIレベルのスレッド/プロセスにマッピング • coordination: • タスク間の通信,依存関係をAPIまたはinstructionレベルの通信・同期に翻訳する

  3. 最も簡単な例 • for (i = 0; i < n; i++) { a[i] = i * i * d;}s = 0;for (i = 0; i < n; i++) { s += a[i]; }

  4. a[0] = … a[1] = … a[2] = … a[n – 1] =… s = 0; for (i = 0; i < n; i++) s += a[i]; Decomposition • 順序制約(依存関係): • A  B  タスクAの終了後にタスクBを開始 •  関係のないものはどのような順番・タイミングで実行されるかわからない(という前提で正しく動くようにdecompositionを行う)

  5. Mapping • タスクをAPIレベルの並列性にマッピング • MPI : • 並列性の源: プロセス(mpirun –np P …) • プロセス数は固定 • Pthreads : • 並列性の源: スレッド(pthread_create) • スレッド数は可変だが,多数作りすぎると性能ロスが大きい • もちろんどの道CPUは固定である

  6. 本例題のタスク  スレッドmapping • P個のスレッド(t = 0, …, P – 1) • スレッドtはi[nt / P, n(t + 1) / P)の更新を行えばよい(等分) スレッド0 スレッド0 スレッド1 スレッド2 スレッドP – 1 … スレッド0

  7. Coordination • 通信 • 実行するタスクが必要とするデータを送る(分散メモリ) • 同期 • 順序制約(依存関係)を満たすための調停 • i.e., 順序制約が満たされたタスクを実行 • 並行アクセスの制御 • 順序制約のない複数のタスクによる,同一メモリロケーションへのアクセスを調停(共有メモリ) • 原子的な更新 • 排他制御

  8. 本例題におけるCoordination(共有メモリ) • initially: run = done = 0; • master() { /* thread 0 */ run = 1; work(0); atomic_increment_n_done(); while (n_done < P); calc_sum();} • worker(t) { /* thread t */ while (run == 0); work(t); atomic_increment_n_done(); }

  9. 本例題におけるCoordination(分散メモリ) • master() { /* processor 0 */for p = 1, …, P – 1 send(p, run); work(0); n_done++; while (n_done < P) { received(done) => n_done++; } calc_sum();} • worker(t) { /* thread t */ receive(run()); work(t); send(master, done); }

  10. より一般的な場合 • タスク間の仕事量がばらつく • 負荷 != タスクの「数」 • タスクの数や形が,プログラムを実行しながら判明する • タスクの数? 負荷? • 対処法: • タスクを細かく分散する(大数の法則) • 動的にmappingをする(動的負荷分散)

  11. 例: Quicksort • sort(a, l, h) { … sort(a, c, h); sort(a, l, c);} l c h

  12. Qucksortのtask sort(0, 100) sort(0, 37) sort(37, 100) sort(0, 10) sort(10, 37) sort(37, 64) sort(64, 100) … … … …

  13. 例2: fib • fib(n) { if (n < 2) return 1; else return fib(n – 1) + fib(n – 2);}

  14. fibのtask fib(5) fib(4) fib(3) fib(3) fib(2) fib(2) fib(1) fib(2) fib(1) fib(1) fib(0) fib(1) fib(0) + + + + + +

  15. スレッド/プロセス  タスクmappingの二つのスタイル • 1スレッド/プロセス  1タスク (細粒度スレッド) • プログラムを単純に記述可能 • スレッド/プロセス CPUのmappingは不確定(OSそのほかに依存) • 1CPU 1スレッド (SPMD) • タスク  スレッド/プロセスのmappingを明示的に記述(プログラムが複雑) • タスク数 >> CPU数のときは唯一の選択肢 • プロセス数固定のモデル(e.g., MPI)でも唯一の選択肢

  16. 一般的なSPMDテンプレート • タスクをあらわす構造体 • タスクの状態: 実行可・不可 • 「実行可能」なタスクを保持する構造体(Runnable Queue, Task Queue, Scheduling Queue) Task Queue

  17. 一般的なSPMDテンプレートの動作 • 各スレッド(= プロセッサ)の動作 • while (!finishsed) {t = get_task(); execute(t);} 新たに{作られた・実行可能になった}task t execute task queue(実行可能task) t t

  18. 各スレッドの動作 • execute(t) {アプリ固有の動作 including … create_task:t’ = a new task created by executing t; mark t’ runnable (e.g., enqueue(t’)); … enable_task:t’ = task enabled by t’; mark t’ runnable (e.g., enqueue(t’));}

  19. Task QueueのDesign Choice • Shared: 全体でひとつ.共有 • Private: スレッドごとにひとつ

  20. Shared Task Queue • 自然に空いているプロセッサにtaskが実行される • ハードウェア共有メモリ以外では非現実的 • ハードウェア共有メモリでも台数が多く,タスク粒度が小さいときはボトルネックになる

  21. Private (per Thread) Task Queue • スレッド(プロセッサ)にひとつのtask queue • スケーラブル • 複雑化する問題: 負荷分散 • Static • Dynamic

  22. Private Task Queue + Static Load Balancing • 全スレッドがofflineに合意できる方法でtaskがどのlocal task queueに入るかを決めておく • e.g., Quicksort : 木のノードの位置を符号化,それをハッシュする • task生成 : 適切なスレッドのqueueにenqueue • 各スレッドは自分のlocal queueからのみtaskを取り出して実行 • taskが細かい粒度でスレッドに分配される

  23. Private Task Queue + Dynamic Load Balancing • task生成: 任意のqueue (ほとんどの場合自分のprivate queue)にtaskをenqueue • 何らかの方針で,private queueの間でtaskを移動 • PULL : 自分のprivate queueにタスクがない場合,他の(e.g., random)スレッドのqueueからtaskを奪う • PUSH : 自分のprivate queueのtaskを時折他のスレッドに移す

  24. Taxonomy Shared Static L.B. Private Lazy (PULL) Dynamic L.B. Proactive (PUSH)

  25. Lazy Task Creation • Private queue + dynamic L.B. + lazy (PULL) load distribution • Task queueはdeque (両端から出し入れ可能なqueue) ローカル実行 負荷分散

  26. Lazy Task Creationの動作 • task生成  先頭にpush; 直ちに実行 • task終了 先頭からpop; 次を実行 • task enabled  末尾に挿入 • 負荷分散  末尾の要素を移動

  27. Lazy Task Creationの効果 … … …

  28. まともな台数効果を得るためのtips (1) • tips以前のtips (Solarisのみ) • pthread_setconcurrency(台数) • thr_setconcurrency(台数) • いずれかを用いて実際の並列度(利用したいCPU数)を指定 • 指定しないと1 (いくらスレッドを作っても無駄)

  29. まともな台数効果を得るためのtips (2) • 世の中はボトルネックだらけ • mutex lockは極力使わない • compare & swapなど,lockを用いないatomic update • どうしてもlockが必要なら,critical sectionを極力短く(< 数十命令)した上でspin lock • 系: libc defaultのmallocを使わない • もっとスケーラブルなライブラリを使う • http://www.yl.is.s.u-tokyo.ac.jp/gc/ • 系: mallocを使ってそうなライブラリ(e.g., STL)を使わない

  30. すでに動的負荷分散機能を持つ言語 • StackThreads/MP, MTCAMP • C/C++のライブラリ, 拡張言語 • http://www.yl.is.s.u-tokyo.ac.jp/mtcamp/ • http://www.yl.is.s.u-tokyo.ac.jp/sthreads/ • Cilk • Cの拡張言語 • http://supertech.lcs.mit.edu/cilk/ • KLIC • 並列論理型言語 • http://www.klic.org/

  31. MTCAMP • Cの拡張 • 情報理工のSunFire15K (istsun0)にインストール済み(利用法は講義HPに近日掲載) • 特徴:多数(>> CPU数)のスレッド生成を許容する

  32. 構文 • 3つの拡張: mtc_sync, mtc_fork, --- • mtc_sync { … mtc_fork [shared(変数名,…)] <文>; … ---;}

  33. • int fib(int n){ if (n < 2) return 1; else { int a, b; ST_POLLING(); mtc_sync { mtc_fork shared(a) a = fib(n - 1); b = fib(n - 2); ---; ST_POLLING(); return a + b; }}}

More Related