• 検索結果がありません。

生物情報実験法 (オンライン, 4/20)

N/A
N/A
Protected

Academic year: 2021

シェア "生物情報実験法 (オンライン, 4/20)"

Copied!
45
0
0

読み込み中.... (全文を見る)

全文

(1)

生物情報実験法

(7/23)

笠原 雅弘 ([email protected])

(2)

Table of Contents

スレッドの使い方

(3)

Deadline

The deadline is Aug 5 23:59

◦ Your e-mail must have reached my e-mail box at the deadline time. It may take a couple of hours to send an e-mail.

◦ Even if you are not able to hand in assignments in time, we accept late

submissions (although with some penalty in grades) so you should try.

◦ You should try optional assignments if you have time.

(4)

スレッドとは

アプリケーションにおいて、複数の

実行コンテキストを作るための仕組み

◦ 大雑把に言うと、プログラムが実行 される「流れ」を複数にする仕組み。 プログラム起動 プログラム終了 スレッド1動作 スレッド2動作 スレッド3動作 生成 合流 合流 生成 時 間 軸

(5)

スレッド生成

(Windows)

2つのスレッドの合流を待つ スレッドを作成する。本来であれば要エラー 対策。 ‘.’ と ‘*’ の 表示順は実行するごと に異なる可能性がある。

(6)
(7)

クラスインスタンスを

スレッドに渡す

スレッドには引数は1つし か 渡せない。 予めデータは s に格納 static 関数のみCreateThread の引 数に渡すことができる。

(8)

マルチスレッドで配列をクリア

大きな配列

・・ ・・ スレッド 0 が 担当して 配列をクリ ア スレッド 1 が 担当して 配列をクリ ア スレッド n-1 が 担当して 配列をクリア 

複数の

CPUを用いてスピードアップ。

(9)

配列クリアの実装(

1/2)

何番目のスレッドか (0 … NUM_THREADS – 1) を記憶 クリアしたい配列の先頭アドレス クリアしたい配列のサイズ(int が 3 要素なら 3 が入 る) スレッドの番号と 配列の先頭アドレス、 配列のサイズを記憶 自スレッドが 担当する範囲を 計算する。 [startp, endp) 実際に配列をクリアする。 使用するスレッド数(≒CPUコア数)。

(10)

配列クリアの実装(

2/2)

今回用いる配列のサイズ NUM_THREADS個のスレッドを生 成 全てのスレッドと 合流するまで待つ 0クリアされていな い 要素が見つかったら エラーを表示する。

(11)

大きな配列要素の和を計算する

大きな配列

・・ ・・ スレッド 0 が 担当して 部分和計算 スレッド 1 が 担当して 部分和計算 スレッド n-1 が 担当して 部分和計算 

スレッドごとに担当領域の部分和を

計算し、最後に部分和の和を計算。

部分和 部分和 ・・ 部分和 ・・ 最後に全体の和を計算

(12)

自分で並列プログラムを

書く際の指針

 なるべく同期はしないほうが良い。 ◦ mutex のロックは速くない。  例えば lock 命令は実行がかなり遅い。  配列の総和を計算する場合の例 ◦ 自スレッド担当分の和をローカルに計算、全体の 和は最後に計算すると良い。  local_sum の計算はスレッド毎に独立していて干渉しない ←必要に応じてここで mutex や CriticalSection を用いる。

(13)

CPU cache とマルチスレッド

 メモリへの書き込みが起こると、書き込み を行っていないCPUはそのアドレスの キャッシュを破棄する必要がある ◦ CPU1が変数 a を読み込み・キャッシュ ◦ CPU2が変数 a を読み込み・キャッシュ ◦ CPU1が変数 a に書き込み  CPU2は変数 a のキャッシュを捨てる CPU1 CPU2 変数 a は書き込んだから 値が変わってるよ! 分かった! 手持ちの 変数 a の 値は捨てとく!

(14)

キャッシュラインと

False Sharing

 同じキャッシュラインに含まれる変数 a と b に対して複数の CPU から書き込みを 行うとお互いにのCPUキャッシュを 無効化してしまい、実効効率が悪化。 ◦ 変数 a と b を格納しているアドレスを 64 で割った 商が同じ場合には同一キャッシュラインの上に 載っている。 ◦ 本当は複数のCPUコアから共有していない変数を 「共有」してしまってキャッシュが実質的に 効かなくなる現象を False Sharing と言う。 変数 a は書き込んだから 値が変わってるよ! 変数 b も同じ ラインに乗って いるから値を 捨てないと… CPU1 CPU2

(15)

スレッドプログラミングの

問題点

記述が面倒

◦ プログラムが長くなる。 ◦ 既存のプログラムの並列化にもかなりの 時間を要するのでやる気が削がれる。 

デバッグが面倒

◦ バグが並列化と絡んでいるのか、 そうでないのか切り分けが難しい。 

保守が面倒

◦ アルゴリズムを変えにくくなる。 ◦ 変化に弱くなる。

(16)

OpenMPとは何か

スレッド並列のプログラムを

簡潔・簡便に書くための言語拡張。

◦ 複数のHPC(High Performance Computing)ベン ダーから成る委員会で策定した各社共通仕様 ◦ http://openmp.org/wp/を参考にしてください ◦ 言語は Fortran/C++が対象であり、複数の コンパイラに実装されている。 

注意

◦ OpenMPI とは名前が非常に良く似ていますが 全く異なるものです。

(17)

OpenMP を使うと嬉しいこと

記述が(スレッドより)ずっと簡単

◦ プログラムが短く書ける。 ◦ 既存のプログラムを並列化するのも 簡単。 

デバッグが(スレッドより)簡単

◦ コンパイルオプション一つで並列版を 非並列版に変更してデバッグできる。 (※適切にプログラムされていれば。) 

保守が(スレッドより)容易

◦ 読みやすくアルゴリズムを変更しやすい。

(18)

OpenMP対応C++コンパイラ

 Visual C++ 2005 以降

◦ 注意:Microsoft Visual Studio 20xx

Express Edition では使えない! ◦ Professional Edition 以降をインストールする。  Intel C++ 11.0 以降  GCC 4.3.1 以降 ◦ Cygwin 環境を用いている人は gcc-4 パッケージを用いても(少なくとも手元で 試した限りでは)動かない。  未確認だがかなり昔からサポートしている はずのコンパイラ ◦ Sun Studio ◦ IBM XL C++ ◦ 日立と富士通の C++ コンパイラは知りません。

(19)

OpenMPの思想

シングルスレッド用に書いた

プログラムに「ディレクティブ」を

加えてマルチスレッド化する。

◦ OpenMP用のプログラムを非OpenMP対応コン パイラでコンパイルするとシングルスレッド 用のプログラムとして動作する。 ◦ シングルスレッドのプログラムを書いてロ ジックが正常動作することを確かめてから並 列化を行うことができる。 

最近ゲノム情報処理界隈では使って

当たり前のテクニックになってきた。

◦ 今後は OpenACC の様なGPGPUハイブリッド 並列化が流行っていくのだろう。

(20)

Fork-Join モデル

シングルスレッド

マルチスレッド

OpenMP)

fork join fork join

(21)

OpenMP のディレクティブ

プログラムの頭の方で

#include <omp.h> を加える。

◦ OpenMP を有効にするコンパイル オプションが指定されていないときには OpenMP の関数を全てダミーに置き換え る。(=OpenMP無効でもコンパイル可) 

“#pragma omp” で始まる行が

OpenMP のディレクティブ。

(22)

まずは基本の並列化

parallel 指示直後のブロックが

マルチスレッドで実行される。

このブロックを並列リージョンと呼ぶ。

何スレッドになるかは環境依存。

◦ 4コアのマシンだと通常は4スレッド。 

スレッド数の手動設定もできる。

スレッド数だけ “Hello” が表示されるはず。

(23)

スレッド数の指定方法

何もしない

◦ スレッド数=CPUコア数になる。 (規格としては実装依存。) 

環境変数

◦ 環境変数OMP_NUM_THREADS にセットした 値だけスレッドを起動する。 

OpenMP の関数を呼ぶ

◦ omp_set_num_threads(n) を呼ぶと n スレッド 起動する。 

parallel ディレクティブにスレッド数を指

定する。(以下の例で

10スレッド。)

(24)

ループの高速化

i=0~ ARRAY_SIZE/4 fork join 4コアのCPUで実行した場合のイメージ i=ARRAY_SIZE/4~

ARRAY_SIZE*2/4 i=ARRAY_SIZE*2/4~ ARRAY_SIZE*3/4 i=ARRAY_SIZE*2/4~ ARRAY_SIZE*4/4

// OpenMP の指示構文(直後の for を並列化する指示)

本当はもう少し賢 く分割できる。

(25)

単純なプログラム例

// OpenMPを用いるプログラムは omp.h を include する

// OpenMP の指示構文(並列化の指示) // OpenMP の指示構文(for文の並列化)

// OpenMP の指示構文(for文の並列化) // 時間が掛かるように無駄に20回ループ。 (並列化の威力が分かりやすくなる。)

(26)

注意点

 注意深く書かれたOpenMPのプログラムは シングルスレッドのプログラムとしても有効。 ◦ 正しく並列化されているか確認しないと実は シングルスレッドで動いている可能性がある。 ◦ C/C++言語の仕様では、#pragma 構文で知らない 単語が出てきた場合には単に無視すれば良いこ とになっている。 (=OpenMPサポートが無い場合には #pragma omp の行は無いのと同じ。)  コンパイラオプションを忘れずに付加する。  gcc なら –fopenmp  VC++ なら /openmp  注意:付けなくてもコンパイル・実行できてしまう! (ただし並列化されない。)

(27)

実行例

2スライド前のプログラムを

Windows

(Visual Studio) 上で実行した例

◦ タスクマネージャを見ると4コアを

(28)

設定

(Windows)

(29)

設定

(Linux)

gcc/icc(g++/icpc)の場合には

(30)

並列リージョン内の変数

並列リージョンで宣言した変数は

基本的にスレッドに固有のものになる。

int a

int b int b int b int b

(31)

スレッド毎に動作を分けたいと

きにどうするか?

 n スレッドで動作しているとき、自分のス レッド番号 (0~n-1)はomp_get_thread_num() で得られる。 ※巨大配列 a を n 分割して、 各パートについて和を求める例。

(32)

for ディレクティブ

並列リージョン中に

#pragma omp for

と書くと、直後の

for 文が複数の

スレッド用に分割されて実行される。

◦ 分割方法が見た目で(コンパイラに) 分からない難しい for 文はダメ

 ○ for(int i = 0; i < n; i++) // nがループ内で不変

 × for(s = a.get(); a.eof(); s = a.get())

◦ ループのイテレーション間に依存関係がある 場合もダメ

(33)

for の例

この行に注目! ループの範囲は [0, VERY_BIG_SIZE) となっているが OpenMP が勝手に スレッド数分の範囲に分割して くれる。

(34)

大きな配列要素の和を計算する(再)

大きな配列

・・ ・・ スレッド 0 が 担当して 部分和計算 スレッド 1 が 担当して 部分和計算 スレッド n-1 が 担当して 部分和計算 

スレッドごとに担当領域の部分和を

計算し、最後に部分和の和を計算。

部分和 部分和 ・・ 部分和 ・・ 最後に全体の和を計算

(35)
(36)

for directive のループ分割

ループタスクの分割方法

◦ #pragma omp for schedule(static, 1000)

 1000 個ずつに分割し、ラウンドロビン方式でス

レッドに割り付ける。

◦ #pragma omp for schedule(dynamic, 1000)

 1000 個ずつに分割し、暇なスレッドが分割された

チャンクを処理していく。

◦ #pragma omp for schedule(guided, 1000)

 未処理の配列個数/スレッド数で分割し、暇なス レッドがチャンクを処理していく。

◦ #pragma omp for schedule(runtime)

(37)

for ブロック後の同期

#pragma omp for の対象となるfor文が

終わるとスレッド間で同期する。

◦ 異なるスレッドから同一変数の値を

参照したときに異なって見えることは (同期後は)なくなる。

◦ 同期する時間が惜しい場合は #pragma omp for nowait として for directive に

nowait を追加することで同期を省くこと もできる。

(38)

reduction 操作

reduction とはある種の構造を持った

演算の総称。

 r = f(…, f(f(x1, x2), x3), x4), … ), xn) の形。 ◦ 合計を求める  r = x1 + x2 + x3 + … + xn  r = add(…, add(add(x1, x2), x3), x4), … ), xn) ◦ 最大値を求める  r = max(…,max(max(x1, x2), x3), x4), … ), xn) ◦ 最小値を求める  r = min(…,min(min(x1, x2), x3), x4), … ), xn) ◦ 積を取る  r = x1 * x2 * x3 * … * xn  r = mul(…, mul(mul(x1, x2), x3), x4), … ), xn)

(39)

reduction + for を簡潔に記述

#pragma omp for の対象となるfor文が

reduction を行う場合には

◦ reduction を行う変数名と演算子を指定す ると競合問題を自動で解決してくれる。

 e.g.) #pragma omp for reduction(+:sum)

◦ 自スレッドに割り当てられた要素のみを 先に reduction し、最後に全体変数に

reduction する。当然ながら、結合律が成 り立たない演算子ではこのような演算は 無理である。

(40)

大きな配列要素の

reduction

大きな配列

・・ ・・ スレッド 0 が 担当して reduction スレッド 1 が 担当して reduction スレッド n-1 が 担当して reduction

スレッドごとに担当領域を

reductionし、

最後に全体の

reduction を計算。

r0 r1 ・・ rn-1 ・・ 最後に f(…, f(f(r0, r1), r2), …), rn-1) を計算。

(41)

sections ディレクティブ

#pragma omp sections ブロックの中に

#pragma omp section ブロックを入れる

と平行して実行してよいタスクを表す。

#pragma omp section #pragma omp sections

#pragma omp section

#pragma omp section

#pragma omp sections {

#pragma omp section { // DO JOB A }

#pragma omp section { // DO JOB B }

#pragma omp section { // DO JOB C }

(42)

同期を伴うディレクティブ

並列リージョン内において、

#pragma omp master の直後に記述された

ブロックはマスタースレッド

(スレッド番号0)でのみ実行される。

並列リージョン内において、

#pragma omp single の直後に記述された

ブロックは(最初に到達した)1つの

スレッドでのみ実行される。

並列リージョン内において、

#pragma omp critical の直後に記述された

ブロックは直列化されて実行される。

◦ critical ブロックの内部に入れるスレッドは 同時に最大1つまでに制限される。

(43)

蛇足

OpenMPが有効になるオプションを

付けてコンパイルされた場合には

_OPENMP マクロが有効になっている

◦ シングルスレッド・マルチスレッドで どうしても異なるコードを書きたいとき には #ifdef _OPENMP などとして コードを分けることができる。

(44)

Assignment 1

Write a program that allocates a large

array of floating point numbers (double).

Fill it with square roots of uniformly

random numbers ranging from 0.0 to 1.0,

using multiple CPU cores.

See how your program scaled with the

number of CPU cores. In other words,

test your program with 1 to max # of

(45)

Assignment 2

Write a program that allocates a large array

of integers. Fill it with 0, 1, 2, …, size -1, using

multiple CPU cores.

See how your program scaled with the

number of CPU cores. In other words, test

your program with 1 to max # of CPU cores,

and see the computation time.

Maybe your program did not scale well as in

the program for Assignment 1.

Answer the reason why your program did

参照

関連したドキュメント

本文書の目的は、 Allbirds の製品におけるカーボンフットプリントの計算方法、前提条件、デー タソース、および今後の改善点の概要を提供し、より詳細な情報を共有することです。

生活のしづらさを抱えている方に対し、 それ らを解決するために活用する各種の 制度・施 設・機関・設備・資金・物質・

Google マップ上で誰もがその情報を閲覧することが可能となる。Google マイマップは、Google マップの情報を基に作成されるため、Google

(ECシステム提供会社等) 同上 有り PSPが、加盟店のカード情報を 含む決済情報を処理し、アクワ

ペットボトルや食品トレイ等のリサイクル の実施、物流センターを有効活用した搬入ト

*+パラメータを Arduino MICRO マイコンでK!す るためのソフト(ソースコード)を Arduino IDE でコンパイルJなMN ( スケッチ )

前ページに示した CO 2 実質ゼロの持続可能なプラスチッ ク利用の姿を 2050 年までに実現することを目指して、これ

化学品を危険有害性の種類と程度に より分類、その情報が一目でわかる ようなラベル表示と、 MSDS 提供を実 施するシステム。. GHS