並列プログラミング入門(OpenMP編)

Similar documents
コードのチューニング

02_C-C++_osx.indd

Microsoft PowerPoint - OpenMP入門.pptx

演習1: 演習準備

PowerPoint プレゼンテーション

OpenMPプログラミング

2. OpenMP OpenMP OpenMP OpenMP #pragma#pragma omp #pragma omp parallel #pragma omp single #pragma omp master #pragma omp for #pragma omp critica

(Microsoft PowerPoint \215u\213`4\201i\221\272\210\344\201j.pptx)

Microsoft Word - openmp-txt.doc

OpenMP¤òÍѤ¤¤¿ÊÂÎó·×»»¡Ê£±¡Ë

NUMAの構成

OpenMP¤òÍѤ¤¤¿ÊÂÎó·×»»¡Ê£²¡Ë

OpenMP¤òÍѤ¤¤¿ÊÂÎó·×»»¡Ê£±¡Ë

AICS 村井均 RIKEN AICS HPC Summer School /6/2013 1

openmp1_Yaguchi_version_170530

03_Fortran_osx.indd

Microsoft PowerPoint - 阪大CMSI pptx

Microsoft PowerPoint - 03_What is OpenMP 4.0 other_Jan18

OpenMP 3.0 C/C++ 構文の概要

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

The 3 key challenges in programming for MC

Microsoft PowerPoint - 阪大CMSI pptx

01_OpenMP_osx.indd

Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. U.S. Government Rights - Commer

Microsoft PowerPoint - OS07.pptx

enshu5_4.key

研究背景 大規模な演算を行うためには 分散メモリ型システムの利用が必須 Message Passing Interface MPI 並列プログラムの大半はMPIを利用 様々な実装 OpenMPI, MPICH, MVAPICH, MPI.NET プログラミングコストが高いため 生産性が悪い 新しい並

Microsoft PowerPoint - 演習1:並列化と評価.pptx

<4D F736F F F696E74202D D F95C097F D834F E F93FC96E5284D F96E291E85F8DE391E52E >

並列計算導入.pptx

演習1

Fortran 勉強会 第 5 回 辻野智紀

概要 プログラミング論 変数のスコープ, 記憶クラス. メモリ動的確保. 変数のスコープ 重要. おそらく簡単. 記憶クラス 自動変数 (auto) と静的変数 (static). スコープほどではないが重要.

Microsoft PowerPoint - 高速化WS富山.pptx

情報処理概論(第二日目)

OpenMP (1) 1, 12 1 UNIX (FUJITSU GP7000F model 900), 13 1 (COMPAQ GS320) FUJITSU VPP5000/64 1 (a) (b) 1: ( 1(a))

Microsoft PowerPoint - 09.pptx

POSIXスレッド

コンピュータ工学講義プリント (7 月 17 日 ) 今回の講義では フローチャートについて学ぶ フローチャートとはフローチャートは コンピュータプログラムの処理の流れを視覚的に表し 処理の全体像を把握しやすくするために書く図である 日本語では流れ図という 図 1 は ユーザーに 0 以上の整数 n

about MPI

Microsoft Word - VBA基礎(3).docx

Microsoft Word - matlab-coder-code-generation-quick-start-guide-japanese-r2016a

2. OpenMP におけるキーワード一覧 OpenMP の全体像を理解するために 指示文 指示節 実行時ライブラリ関数 環境変数にそれぞれどうようなものがあるのかを最初に示します 各詳細については第 4 章以降で説明します 2.1 OpenMP の指示文 OpenMPの指示文は プログラム内で並列

NUMAの構成

memo

OpenMPプログラミング

演習準備

ÊÂÎó·×»»¤È¤Ï/OpenMP¤Î½éÊâ¡Ê£±¡Ë

ex04_2012.ppt

Microsoft PowerPoint - 計算機言語 第7回.ppt

.NETプログラマー早期育成ドリル ~VB編 付録 文法早見表~

生成された C コードの理解 コメント元になった MATLAB コードを C コード内にコメントとして追加しておくと その C コードの由来をより簡単に理解できることがよくありま [ 詳細設定 ] [ コード外観 ] を選択 C コードのカスタマイズ より効率的な C コードを生成するベストプラクテ

1.overview

スライド 1

RX ファミリ用 C/C++ コンパイラ V.1.00 Release 02 ご使用上のお願い RX ファミリ用 C/C++ コンパイラの使用上の注意事項 4 件を連絡します #pragma option 使用時の 1 または 2 バイトの整数型の関数戻り値に関する注意事項 (RXC#012) 共用

Java講座

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

また RLF 命令は 図 2 示す様に RRF 命令とは逆に 各ビットを一つずつ 左方向に回転 ( ローテイト ) する命令である 8 ビット変数のアドレスを A とし C フラグに 0 を代入してから RLF A,1 を実行すると 変数の内容が 左に 1 ビットシフトし 最下位ビット (LSB)

講習No.9

この方法では, 複数のアドレスが同じインデックスに対応づけられる可能性があるため, キャッシュラインのコピーと書き戻しが交互に起きる性のミスが発生する可能性がある. これを回避するために考案されたのが, 連想メモリアクセスができる形キャッシュである. この方式は, キャッシュに余裕がある限り主記憶の

Microsoft Word - VBA基礎(6).docx

PowerPoint Presentation

Microsoft PowerPoint - compsys2-06.ppt

memo

JavaプログラミングⅠ

VelilogHDL 回路を「言語」で記述する

gengo1-11

kiso2-09.key

Microsoft PowerPoint - ARCEMB08HayashiSlides.ppt [互換モード]

ex05_2012.pptx

<4D F736F F D20332E322E332E819C97AC91CC89F090CD82A982E78CA982E9466F E393082CC8D5C91A291CC90AB945C955D89BF5F8D8296D85F F8D F5F E646F63>

Microsoft PowerPoint - sales2.ppt

フローチャートの書き方

Microsoft PowerPoint - ca ppt [互換モード]

講習No.1

プレポスト【問題】

GPU チュートリアル :OpenACC 篇 Himeno benchmark を例題として 高エネルギー加速器研究機構 (KEK) 松古栄夫 (Hideo Matsufuru) 1 December 2018 HPC-Phys 理化学研究所 共通コードプロジェクト

プログラミングI第10回

PowerPoint プレゼンテーション

Microsoft PowerPoint - 10.ppt [互換モード]

Visual C++ 使用 C 言語 ワンポイント講座 デバッガで言語文法を理解する M.NET.SC デバッガで言語仕様 ( 文法 ) の実体 ( コンパイラの解釈と振る舞い ) を理解する プログラムでできることを習得する

Microsoft PowerPoint - sales2.ppt

memo

7 ポインタ (P.61) ポインタを使うと, メモリ上のデータを直接操作することができる. 例えばデータの変更 やコピーなどが簡単にできる. また処理が高速になる. 7.1 ポインタの概念 変数を次のように宣言すると, int num; メモリにその領域が確保される. 仮にその開始のアドレスを 1

SuperH RISC engineファミリ用 C/C++コンパイラパッケージ V.7~V.9 ご使用上のお願い

PowerPoint プレゼンテーション

COMET II のプログラミング ここでは機械語レベルプログラミングを学びます 1

gengo1-8

sinfI2005_VBA.doc

Microsoft Word ●IntelクアッドコアCPUでのベンチマーク_吉岡_ _更新__ doc

4-4 while 文 for 文と同様 ある処理を繰り返し実行するためのものだが for 文と違うのは while 文で指定するのは 継続条件のみであるということ for 文で書かれた左のプログラムを while 文で書き換えると右のようになる /* 読込んだ正の整数値までカウントアップ (for

Microsoft PowerPoint - 11.pptx

PowerPoint プレゼンテーション

Slides: TimeGraph: GPU Scheduling for Real-Time Multi-Tasking Environments

インテル(R) Visual Fortran Composer XE 2013 Windows版 入門ガイド

JavaプログラミングⅠ

プログラミング実習I

書式に示すように表示したい文字列をダブルクォーテーション (") の間に書けば良い ダブルクォーテーションで囲まれた文字列は 文字列リテラル と呼ばれる プログラム中では以下のように用いる プログラム例 1 printf(" 情報処理基礎 "); printf("c 言語の練習 "); printf

Transcription:

登録施設利用促進機関 / 文科省委託事業 HPCI の運営 代表機関一般財団法人高度情報科学技術研究機構 (RIST) 1 並列プログラミング入門 (OpenMP 編 ) 2019 年 1 月 17 日 高度情報科学技術研究機構 (RIST) 山本秀喜

RIST 主催の講習会等 2 HPC プログラミングセミナー 一般 初心者向け : チューニング 並列化 (OpenMP MPI) 京 初中級者向け講習会 京 利用者 利用予定者向け : 京 に特化した内容 ワークショップ等 一般 経験者向け : ユーザー間の情報共有 RIST 主催 共催の講習会 セミナー ワークショップ一覧 https://www.hpci-office.jp/pages/seminar RIST 講習会 で出てきます 多分

Outline 3 はじめに ( 現在の HPC について ) 並列処理 OpenMP 入門 並列実行領域中のデータの属性 アクセス競合に注意すべきループ データ共有属性ミスの例 ループの並列化と依存性 Reduction 演算 DO ループのスケジューリング 並列の制御 同時処理 実行時ライブラリルーチンと環境変数 OpenMP 並列化例

はじめに 4 現在の HPC 用コンピュータの形態 複数のコンピュータ群からなる 1 台 1 台をノードと呼ぶ 性能を活かすにはノード間の並列化が必要 参考 : 京 は約 8 万ノード ノード CPU Core Core Core Core メモリ ノード CPU Core Core Core Core メモリ マルチコア CPU( メニーコア ) CPU 内に複数のプロセッサーコア パソコンでも主流 性能を活かすにはノード内の並列化が必要 参考 : 京 は 8 コア CPU が 1 個 / ノード ノード CPU ネットワーク ( インターコネクト ) メモリ ノード CPU メモリ Core Core アクセラレータ ( 省略 ) Graphics Processing Unit(GPU) Core Core Core Core Core Core 本日の題目ノード内並列化に多く用いられる OpenMP の入門的内容 ( ノード間並列化に使われる MPI はこの後 )

5 並列処理 並列処理の形態を説明します

並列処理とは 6 逐次 4 並列 処理処理処理処理 処理処理処理処理 処理を分割して同時並列に実行すること処理終了までの時間の短縮が目的マルチコア環境では各コアに処理を分配したい 時間

プロセスとスレッド 7 スレッド プロセスより小さい実行単位 ( 処理の分割単位 ) プロセス スレッド スレッド プロセス スレッド メモリ空間 逐次実行中 ( 通常 ) のプロセス ( シングルスレッド ) メモリ空間 並列実行中のプロセス (2 スレッド ) 1 プロセス内のスレッドはメモリ空間を共有する 1 つのスレッドは 1 つのコアで実行される

プロセスとスレッド (cont.) 8 スレッド プロセスより小さい実行単位 ( 処理の分割単位 ) CPU CPU コアコアコアコア スレッドスレッドスレッド 稼働中 空き 稼働中 稼働中 1 スレッドプロセスを処理中の 2 コア CPU 2 スレッドプロセスを処理中の 2 コア CPU マルチスレッドのプロセスはマルチコアの性能を引き出せる

おもな並列化方式 9 スレッド並列 プロセス並列 プロセス プロセス プロセス スレッド スレッド スレッド スレッド メモリ空間 メモリ空間 プロセス間通信 メモリ空間 OpenMP 自動並列 ノード内の並列 ( 共有メモリ並列 ) MPI(Message Passing Interface) ノード間の並列 ( 分散メモリ並列 ノード内の並列も可 )

おもな並列化方式 (cont.) 10 ノード ハイブリッド並列 (OpenMP+MPI) ノードプロセスプロセス スレッド スレッド スレッド スレッド メモリ空間 プロセス間通信 メモリ空間 例えば 京 コンピュータでは 通信量の削減の観点から ノード内を OpenMP 並列または自動並列 ノード間を MPI 並列 という両者を組み合わせたハイブリッド並列が推奨されている

11 OpenMP 入門 OpenMP の Hello world プログラムと DO ループの並列化を紹介します

OpenMP による並列化 12 do i = 1, 4000 A(i) = B(i) + C(i)!$omp parallel do do i = 1, 4000 A(i) = B(i) + C(i)!$omp end parallel do OpenMP 構文による並列化 ループを挟むように構文 ( ディレクティブ ) を挿入

代表的な OpenMP 構文 (Fortran) 13 代表的なOpenMP 構文 (Fortran)!$omp parallel /!$omp end parallel!$omp do!$omp parallel do!$omp parallel do reduction(+: )!$omp critical!$omp barrier!$omp single!$omp ordered

代表的な OpenMP 構文 (C/C++) 14 代表的な OpenMP 構文 (C/C++) #pragma omp parallel #pragma omp for #pragma omp parallel for #pragma omp parallel for reduction(+: ) #pragma omp critical #pragma omp barrier #pragma omp single #pragma omp ordered #pragma omp parallel for for ( i=0 ; i<4000 ; i++ ) { A[i] = B[i] + C[i]; }

並列実行領域 (parallel 構文 ) 15 parallel 構文!$omp parallel [ 指示節 [, 指示節 ]] parallel~end parallel で囲まれた領域を並列実行します program hello write(*,*) Hello world!$omp parallel write(*,*) Hello OpenMP world!$omp end parallel end 並列実行領域 (parallel region)

実行例 :Hello OpenMP world 16 $ frt -Kopenmp hello.f (*) $ export OMP_NUM_THREADS=4 (**) $./a.out 実行 Hello world 逐次部分からの出力 Hello OpenMP world Hello OpenMP world Hello OpenMP world Hello OpenMP world $ OpenMP オプションをつけてコンパイル スレッド数 ( 並列数 ) を環境変数で設定 並列実行領域の出力 (4 並列実行 ) コンパイラによってオプションが違う (*) intel fortran: ifort openmp hello.f GNU: gfortran fopenmp hello.f 京 login-node: frtpx Kopenmp hello.f (**) csh の場合 : setenv OMP_NUM_THREADS 4

OpenMP スレッドの動作 17 マスタースレッド プログラム開始 逐次実行領域 分岐 (fork): スレッドチーム結成 ( ワーカースレッド生成 )!$omp parallel スレッド 0 = マスタースレッド スレッド 1 スレッド 2 スレッド 3 並列実行領域 合流 (join): スレッドチーム消滅 ( ワーカースレッド消滅 ) マスタースレッド!$omp end parallel 逐次実行領域 プログラム終了

パラレル構文の効果 18 逐次 処理 並列? 処理 Parallel 構文 スレッドの分岐 合流を制御 処理の割り振りはしない 処理 処理 処理 処理 並列化には処理の割り振り が必要 時間 処理 処理 後述のワークシェアリング構文を利用する

ワークシェアリング構文 19 Parallel 構文は スレッドチームの分岐 / 合流を制御する 並列化には さらに 処理の割り振り ( ワークシェアリング ) が必要 OpenMP ではワークシェアリング構文を用いる ワークシェアリング構文の種類 ループ構文 do ループを分割実行 single 構文 生成されたスレッドのうち 1 つのスレッドのみが実行 sections 構文 依存関係のない異なる処理をそれぞれのスレッドで実行 workshare 構文 (Fortran のみ ) fortran90 以降の配列代入文などを分割実行 本資料では ループ構文を主に扱います

DO ループのワークシェアリング 20 do 構文 ( ループ構文 ) 並列実行領域において do ループを分割し チーム内の各スレッドに割り当てます デフォルトでは 均等に分割され 各スレッドにより実行されます!$omp parallel!$omp do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp!$omp end parallel 逐次 1 2 3 ループ長 n=4000 n=4000 4 並列で実行 4 並列 1000 2000 3000 4000 スレッド 0 が実行スレッド 1 が実行スレッド 2 が実行スレッド 3 が実行

DO ループのワークシェアリングの 21 書式 (Fortran) do i = 1, 4000 V(i) = X(i) + Y(i)!$omp parallel!$omp do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp!$omp end parallel この DO ループを並列化する パラレル構文!$omp parallel [ 指示節 [, 指示節 ]] 並列実行領域!$omp end parallel ( 省略不可 ) parallel~end parallel で囲まれた領域を並列実行 ループ構文 (DO 構文 )!$omp do [ 指示節 [, 指示節 ]] do ループ [!$omp ] ( 省略可能 )!$omp parallel do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp end parallel do パラレルループ構文 ( パラレル構文とループ構文の複合 )!$omp parallel do [ 指示節 [, 指示節 ]] doループ [!$omp end parallel do] ( 省略可能 ) 後続のdoループを各スレッドで分割して並列実行します

22 DO ループのワークシェアリングの 書式 (Fortran) の省略形 do i = 1, 4000 V(i) = X(i) + Y(i) この DO ループを並列化する!$omp parallel!$omp do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp!$omp end parallel!$omp は省略可能です!$omp parallel!$omp do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp end parallel!$omp parallel do do i = 1, 4000 V(i) = X(i) + Y(i)!$omp end parallel do!$omp end parallel do を省略すると!$omp parallel do do i = 1, 4000 V(i) = X(i) + Y(i)

For ループのワークシェアリングの 23 書式 (C 言語 ) for ( i=0 ; i<4000 ; i++ ) { V[i] = X[i] + Y[i]; } この for ループを並列化する #pragma omp parallel { #pragma omp for for ( i=0 ; i<4000 ; i++ ) { V[i] = X[i] + Y[i]; } } パラレル構文 (C 言語 ) #pragma omp parallel [ 指示節 [, 指示節 ]] 後続の領域を並列実行します ループ構文 (for 構文 )(C 言語 ) #pragma omp for [ 指示節 [, 指示節 ]] 後続のforループを分割して各スレッドに割り当てます #pragma omp parallel for for ( i=0 ; i<4000 ; i++ ) { V[i] = X[i] + Y[i]; } パラレルループ構文 (C 言語 ) #pragma omp parallel for [ 指示節 [, 指示節 ]] 後続の for ループを各スレッドで分割して並列実行します

24 並列実行領域中のデータの属性 スレッド間でのデータの共有属性 (shared 属性とprivate 属性 )

並列実行領域中のデータの属性 25 データ共有属性 ( 並列領域内の変数 配列の属性 ) Sharedデータ : 全てのスレッドからアクセス可能なデータ Privateデータ : 各スレッド固有の 他のスレッドからは見えないデータ!$omp parallel do do i = 1, 4000 V(i) = X(i) + Y(i) スレッド スレッド private データ i shared データ V(:), X(:), Y(:) private データ i OpenMP では データ共有属性をプログラマの責任で設定する必要があります 誤った設定は 不正な結果 ( バグ ) の原因となります

データ共有属性の宣言 26 データ共有属性の宣言 parallel 構文や do 構文の指示節として指定します!$omp parallel do private(i) shared(v, X, Y) do i = 1, 4000 V(i) = X(i) + Y(i) 暗黙のデータ共有属性 並列実行領域において 指定の無いほとんどのデータは デフォルトで shared 属性 ( 詳細は後述 ) parallel do または do 構文のループ内のループインデックス変数は その構文内で private 属性 暗黙のデータ共有属性により 上記の例では private(i) shared(v, X, Y) を省略可

Shared 属性 27 Shared データ すべてのスレッドから参照可能 並列実行領域開始前と同一の ( メモリ領域に記憶される ) 変数 プログラム開始 マスタースレッド 0 V X Y shared(v, X, Y) shared 指示節で指定されたデータ あるいは暗黙の shared 属性データ マスタースレッド0 スレッド1 スレッド2 スレッド3 並列実行領域

Private 属性 28 Private データ 各スレッドに固有のデータ 他のスレッドからはアクセス不可 並列実行領域前の対応する変数とは別の実体 ( メモリー領域 ) を持つ 初期値は未定義 プログラム開始 マスタースレッド 0 i V X Y shared(v, X, Y) マスタースレッド0 スレッド1 スレッド2 スレッド3 i 0 i 1 i 2 i 3 互いにアクセスすることはできない 例えば i 0 はスレッド 0 に固有の i を表す private( i ) Private 指示節で指定されたデータ

暗黙のデータ共有属性 (Fortran) 29!$omp parallel!$omp do do i=1,4 X(i)=i call sub(x(i))!$omp!$omp end parallel 並列実行領域内 Private: ループ構文内のループインデックス ( i ) 他にParallel 構文内の逐次 DOループインデックス DO 型反復のインデックスもprivate Shared: 何の指定もない変数 ( X(:) ) default 指示節により変更可 ( 暗黙のデータ共有属性 ) default(shared), default(private), default(none) none の場合は明示的に指定しなければならない subroutine sub(y) common /com/ n save ymax real :: a = 1.0 real :: b end コールされたルーチン内 仮引数はcall 元のルーチンに従う yのアドレスはcall 元のX(i) を指しているので その設定に従う Shared: COMMON/SAVE 文の変数 (n, ymax, a) 他にmoduleやsave 属性の変数もshared Private: 上記以外の変数 (b) a = 1.0 のように初期値を与えると save 属性 詳しくは仕様書を参照してください

30 暗黙のデータ共有属性 (Fortran) 並列実行領域内!$omp parallel!$omp do do i=1,4 X(i)=i call sub(x(i))!$omp!$omp end parallel ループ構文内のループインデックス ( i ) は PRIVATE 他に parallel 構文内の逐次の do ループインデックスや do 型反復のインデックスも PRIVATE 上記以外の何の指定もない変数 ( X(:) ) は SHARED この 暗黙のデータ共有属性 は default 指示節 により変更可能 default(shared), default(private), default(none) none の場合は 明示的に指定しないとエラーになる

31 暗黙のデータ共有属性 (Fortran) コールされたルーチン内 subroutine sub(y) common /com/ n save ymax real :: a = 1.0 real :: b end 仮引数は call 元のルーチンに従う y のアドレスは call 元の X(i) を指しているので その設定に従う common/save 属性の変数 (n, ymax, a) は SHARED 他に module 変数も SHARED 初期値指定のある変数も save 属性なので要注意 上記以外 (b) は PRIVATE 厳密な規則は仕様書を参照してください この例は スレッドセーフではありません

32 アクセス競合に注意すべきループ データ共有属性ミスの例 データ共有属性の設定ミス アクセス競合

一時変数を含むループ 33 private 属性の指定忘れに注意!$omp parallel do do i = 1, 4 t = X(i) + Y(i) V(i) = V(i) + t * t スレッド 0 t = X(1) + Y(1) V(1) = V(1) + t * t t = X(2) + Y(2) V(2) = V(2) + t * t 同時更新 t スレッド 1 t = X(3) + Y(3) V(3) = V(3) + t * t t = X(4) + Y(4) V(4) = V(4) + t * t t を private 属性指定しないと t は shared 属性なので t はそれぞれのスレッドから同時更新され タイミングによって結果が異なってしまいます!$omp parallel do private( t )

一時変数を含むループ (cont.) 34 private 属性指定の見落とし - 右の例ではループインデックス i 以外 すべてshared 属性となってしまう マスタースレッド 0 t = t * t プログラマが期待? した動作 スレッド0が更新スレッド0が結果を読込スレッド1が更新スレッド1が結果を読込!$omp parallel do do i = 1, 4000 t = X(i) + Y(i) V(i) = V(i) + t * t shared(t, V, X, Y) t t V X Y 各スレッドの更新 読込に 運良く重なりが無ければ正しい結果となる

一時変数を含むループ (cont.) 35 private 属性指定の見落とし - 右の例ではループインデックス i 以外 すべてshared 属性となってしまう マスタースレッド 0!$omp parallel do do i = 1, 4000 t = X(i) + Y(i) V(i) = V(i) + t * t t = スレッド 0 が更新 意図しないタイミングでの t の更新が発生する可能性があり 時々不正な結果となる 別スレッドの意図せぬ更新 shared(t, V, X, Y) t t V X Y t * t スレッド 0 が不正な結果を読込 異常終了せず 常に不正な値を与えるわけではないので 表面化しにくい

一時変数を含むループ ( 不正の回避 ) 36 private 属性を正しく指定する - 左辺にあって複数のスレッドが更新する変数は private にする!$omp parallel do private( t ) do i = 1, 4000 t = X(i) + Y(i) V(i) = V(i) + t * t プログラム開始 マスタースレッド 0 V X Y shared(v, X, Y) マスタースレッド 0 スレッド 1 スレッド 2 スレッド 3 t 0 t 1 t 2 t 3 private( t ) プログラムは正常に動作します

サブルーチンコールのあるループ 37!$omp parallel do do i = 1, 4 call SUB( ) Subroutine SUB( ) call SUB2( ) end Subroutine SUB2( ) COMMON /COM/ WORK(100) WORK(1) = = WORK(1) end サブルーチン内でも common の変数は shared スレッド 0 スレッド 1 WORK(1) = = WORK(1) 同時更新の恐れあり WORK(1) サブルーチン内部の common や save 属性の変数に注意してください WORK(1) = = WORK(1) 上記の例では 他のサブルーチンで /COM/ を利用している場合は threadprivate 構文で private 化します ( 詳細はおまけの 共有変数がある場合の注意 を参照 )

スレッドセーフとは ( 競合と排他制御 ) 38 スレッドセーフなルーチン 複数のスレッドで同時に実行しても意図した機能を果たすルーチン 例えば 間接参照を含むルーチンで同一のアドレスをアクセスしてしまうような処理は 競合が発生しており スレッドセーフではなくなります スレッドセーフでない処理の例 : COMMON や SAVE 属性の変数にアクセスしている関数 サブルーチンで 排他制御が正しくできていないルーチン ローカル変数であっても コンパイル時に -save オプションを付加した場合 同一のユニット番号に対する READ や WRITE などの I/O 処理 (Fortran の規格ではスレッドセーフを保証していません ) 本資料では理解しやすさのため 並列実行領域内で write 文を実行するプログラム例が多数ありますが 本来は critical 構文等で排他制御をすべきです スレッドセーフでない処理を並列実行してしまうと 計算結果が不正だったり プログラムが異常終了する場合があります 常に発生するとは限らない上 実行環境により頻度が変わるため 問題が発見しにくい場合があります

39 OpenMP 化によるミスの特徴 PRIVATE/SHARED 属性宣言のミス ( アクセス競合 ) 不正な結果 ( バグ ) の原因 実行の度に結果が異なる場合がある 発生頻度は 2 回に 1 度のこともあれば 数千回に 1 回のことも 発見しにくい OpenMP ではデータ共有属性を注意して設定する必要がある コンパイラによっては警告を出してくれることもある

40 ループの並列化と依存性 並列化してはダメなループ

41 並列化できないループ 並列化してはダメなループ ループ構文で並列化できないループ : exit 等 途中でループを抜ける命令があるループ goto~continue のループ構造 等 コンパイルできない!$omp parallel do do i = 1, N if ( ) exit A(i) = A(i) + 1.0 並列化してはいけないループ ( 次スライドで説明 ): 他のサイクルの結果を参照するループ = 反復 ( サイクル ) 間に依存性のあるループ OpenMP で 並列 実行できてしまうが 逐次実行と異なる結果を与えてしまう!$omp parallel do do i= 2, N V(i) = V(i - 1) + a

42 並列化してはいけない例 (1) フロー依存のあるループ ( 逐次 ) do i = 2, 7 V(i) = V(i - 1) + a 上のサイクルの結果を利用して計算している 例えば i=5の計算 : V(5) は サイクル i=4 の結果の V(4) を用いて計算する 逐次実行 i=2: V(2) = V(1) + a i=3: V(3) = V(2) + a i=4: V(4) = V(3) + a i=5: V(5) = V(4) + a i=6: V(6) = V(5) + a i=7: V(7) = V(6) + a 分割 フロー依存とは Write After Read

43 並列化してはいけない例 (1) フロー依存のあるループ!$omp parallel do do i = 2, 7 V(i) = V(i-1) + a!$omp parallel do private(t) do i = 2, 7 t = V( i 1 ) V( i ) = t + a 一時変数を使っても同じ スレッド 0 スレッド 1 V(2) = V(1) + a V(3) = V(2) + a V(4) = V(3) + a 最後に V(4) の計算が完了 計算結果が実行順序に依存 V(5) = V(4) + a 最初に更新後の V(4) の値が必要 V(6) = V(5) + a V(7) = V(6) + a ( 注 ) スレッド 0 が V(4) を計算する前に スレッド 1 が V(4) を参照してしまいます

44 並列化してはいけない例 (2) 逆依存のあるループ ( 逐次 ) do i = 1, 6 V(i) = V(i+1) + a 上のサイクルで使用した要素を上書きしながら計算している 例えば i=3の計算 : V(3) は 更新前の V(4) を用いて計算し 次の i=4 で V(4) は上書きされます 逐次実行 i=1: V(1) = V(2) + a i=2: V(2) = V(3) + a i=3: V(3) = V(4) + a i=4: V(4) = V(5) + a i=5: V(5) = V(6) + a i=6: V(6) = V(7) + a 分割 逆依存とは Read After Write

45 並列化してはいけない例 (2) 逆依存のあるループ!$omp parallel do do i = 1, 6 V(i) = V(i+1) + a!$omp parallel do private(t) do i = 1, 6 t = V( i +1 ) V( i ) = t + a 一時変数を使っても同じ スレッド0 スレッド 1 V(1) = V(2) + a V(2) = V(3) + a V(3) = V(4) + a 最後に更新前の V(4) が必要 計算結果が実行順序に依存 V(4) = V(5) + a 最初に V(4) の値を上書き V(5) = V(6) + a V(6) = V(7) + a ( 注 ) スレッド 0 が V(4) の元の値を参照とする前に スレッド 1 が先に V(4) の値を更新してしまいます

46 並列化してはいけない例 (3) 重なりのある間接参照のあるループ 間接参照のあるループ?!$omp parallel do do i = 1, 4 V( List( i ) ) = X(i) + Y(i) スレッド 0 V( List(1) ) = X(1) + Y(1) V( List(2) ) = X(2) + Y(2) 同じ要素を更新する可能性 V スレッド 1 V( List(3) ) = X(3) + Y(3) V( List(4) ) = X(4) + Y(4) 例えば List(2) = List(4) の場合 どちらの間接参照が後にアクセスされるかによって結果が変化します ( 順番に依存 ) 配列 List( ) の値がすべて異なる ( ユニークな ) 場合を除き並列化してはいけません

47 Reduction 演算 総和などの計算をおこなう reduction 指示節を 説明します

総和計算の並列化 48 総和の計算を並列化する!$omp parallel do do i = 1, 4 S = S + V(i) スレッド 0 スレッド 1 S = S + V(1) S = S + V(2) 同時更新の可能性 S が shared 属性ならば スレッド 0 とスレッド 1 がそれぞれ勝手なタイミングで S の値を更新するため 結果は不定となります もし S を private 属性にすると 全体の総和を得ることはできません S 解決するには? S = S + V(3) S = S + V(4)

総和計算の並列化 (cont.) 49 総和の計算を並列化する reduction 指示節!$omp parallel do reduction (+: S) do i = 1, 4 S = S + V(i) S は特殊な private 変数として扱われる (reduction 変数 ) スレッド 0 スレッド 1 S 0 = 0 S 0 = S 0 + V(1) S 0 = S 0 + V(2) 初期値 S 1 = 0 S 1 = S 1 + V(3) S 1 = S 1 + V(4) S = S + S0 + S1 元の変数に加算 ( 注 )Reduction 演算は 計算の順序が逐次演算と異なります そのため 丸め誤差により結果が微妙に異なる可能性があります 数値計算的には V(1) + V(2) + V(3) +V(4) { V(1) + V(2) } + { V(3) +V(4) } 加算順序はスレッド番号順とは限りません

Reduction 指示節 50 総和 ( 足し算 ) 以外の演算にも reduction 指示節が使えます!$omp parallel do reduction(op : r 1 [, r 2 ] ) op : reduction 演算子 (+, *, -,.and.,.or.,.eqv.,.neqv., max, min, iand, ior, ieor) r 1 [, r 2 ] : reduction 変数 ( 複数指定可 ) Reduction 演算 複数の変数を何らかの演算で一個の変数に縮約する操作 一般例 : r = r op expr の繰り返し等 (r: reduction 変数, expr: rを参照しない式 ) Reduction 指示節により ループ内で reduction 変数の private なコピーを作成し ループ終了後 各スレッドの演算結果を元の変数に縮約する

Reduction 指示節 (cont.) 51 表 Reduction 変数の演算と初期値 演算初期値演算初期値 + 0.neqv..false. * 1 max - 0 min 変数の型で表せる最小の値 変数の型で表せる最大の値.and..true. iand すべてのビットが 1.or..false. ior 0.eqv..true. ieor 0 Reduction 変数は ループ内では privateな一時変数として扱われます ループ開始時に演算子の種類に応じて適切に初期化されます

52 DO ループのスケジューリング オーバーヘッドとロードバランス そして OpenMPで用意されているスケジューリングの方法について説明します

オーバーヘッド 53 並列化によって プログラムの実行時間を短縮することができますが 逐次 4 並列 時間並列化 オーバーヘッド 1/4 オーバーヘッド 1 並列化にはオーバーヘッドがつきものです スレッド生成 同期 並列化に伴うコード変更による処理の増加等 完全な並列化をおこなってもオーバーヘッドのため 1/4 にはならない オーバーヘッドが無視できる程度の大きい処理を並列化すべき ( 多重ループならば外側が望ましい )

オーバーヘッド (cont.) 54!$omp parallel do do i = 1, 10!$omp end parallel do!$omp parallel do do i = 1, 1000000!$omp end parallel do 反復数の少ないループより多いループの方がオーバーヘッドが相対的に小さくなります do j = 1, n!$omp parallel do do i = 1, n!$omp end parallel do!$omp parallel do do j = 1, n do i = 1, n!$omp end parallel do 内側ループより外側ループを並列化した方が!$omp parallel do の呼び出し回数が少なくオーバーヘッドが少なくなります

ロードバランス 55 均等な処理の割り振り (load balancing) にも注意する必要があります 時間 逐次 並列化 4 並列 1/4 idle idle idle 1 不均等 ( インバランス ) な割振りでは 期待した性能が出ないことがあります

ループ構文とスケジューリング 56 Schedule 指示節により ループ反復の割当方法を変更できます!$omp parallel do schedule( スケジューリングの種別 ) スケジューリング指示節割当方法 schedule(static) 均等に分割 <デフォルト> schedule(static, chunk) schedule(dynamic [,chunk ] ) schedule(guided [,chunk ] ) schedule(auto) schedule(runtime) chunk で指定した反復数のチャンクに分割し スレッド番号順に巡回的に割り当てます chunk で指定した反復数のチャンクに分割し スレッドからの要求に応じて動的に割り当てます 各スレッドは 1 チャンクを実行し 次のチャンクを要求します <chunk 省略時は chunk=1> dynamic と同様ですが チャンクの大きさが残りの反復数に応じて徐々に小さくなります チャンク分割サイズは chunk で指定した値が最小になります <chunk 省略時は chunk=1> スケジューリングは コンパイラ および / または 実行時システムに委ねられます スケジューリングは 実行時の環境変数 OMP_SCHEDULE によって決定されます 例 : export OMP_SCHEDULE= guided, 1

STATIC( 静的 ) スケジューリング 57!$omp parallel do schedule( スケジューリングの種別 ) Schedule 指示節により ループ反復の割当方法を変更できます 逐次 全てをマスタースレッドが処理 静的割り当て : 実行前に割り当てを決める schedule(static) デフォルト schedule(static, 1) schedule(static, 2) 均等に分割 <デフォルト> サイズ1のチャンクに分割し 順繰りに配分サイズ2のチャンクに分割し 順繰りに配分 ( はループの 1 反復 (1 サイクル ) を示す )

DYNAMIC( 動的 ) スケジューリング 58!$omp parallel do schedule( スケジューリングの種別 ) Schedule 指示節により ループ反復の割当方法を変更できます 動的割り当て : 処理の終わったスレッドが次のチャンクに取りかかる サイクルごとの処理量が不均一な時に効果的だが static スケジューリングよりオーバーヘッドが大きい schedule(dynamic, 2) thread0 thread1 thread2 thread3 thread2 thread3 thread1 thread0 thread2 thread3 thread1 サイズ 2 のチャンクに分割し 動的に割り当て ( デフォルトのチャンクサイズは 1) thread0

スケジューリングとロードバランス 59 三角行列とロードインバランス do j = 1, n do i = j, n A(i, j) = A(i, j) + B(i, j) j!$omp parallel do do j = 1, n do i = j, n A(i, j) = A(i, j) + B(i, j) 単純に 4 並列実行 i スレッド 0 1 2 3 が担当 処理量に差 ロードインバランス状態

スケジューリングとロードバランス (cont.) 60 ロードバランスの改善!$omp parallel do schedule(static, 1) do j = 1, n do i = j, n A(i, j) = A(i, j) + B(i, j)!$omp parallel do schedule(dynamic) do j = 1, n do i = j, n A(i, j) = A(i, j) + B(i, j) スレッド 0 1 2 3 0 1 2 3 0 1 2 3 が担当 スレッド 0 1 2 3 3 2 1 0 1 0 3 2 が担当 チャンクを細かくするとデフォルトの static よりロードバランスが改善し 負荷が均等になります Dynamic は完璧なように思えますが オーバーヘッドが大きいので注意が必要です

61 並列の制御 同期処理 並列処理領域内の逐次処理同期処理排他制御 排他制御 同期処理

暗黙の同期 62 ループ構文などのワークシェアリング構文の出口では 暗黙に同期処理が行われます!$omp parallel!$omp do do i = 1, n V(i) = V(i) + X(i)!$omp 暗黙の同期!$omp do do i = 1, n W(i) = W(i) + Y(i)!$omp 暗黙の同期!$omp end parallel マスタースレッド 0 1 2 3 待ち合わせ 待ち合わせ 各 DO ループの終了時に 全スレッドの処理終了を待ってから 次の DO ループの処理に移ります 待ち合わせのための若干のオーバーヘッドがかかります

暗黙の同期の回避 (nowait 指示節 ) 63 暗黙の同期処理が不要ならば nowait 指示節により 同期を回避できます!$omp parallel!$omp do do i = 1, n V(i) = V(i) + X(i)!$omp nowait!$omp do do i = 1, n W(i) = W(i) + Y(i)!$omp 暗黙の同期!$omp end parallel 同期を回避 マスタースレッド 0 1 2 3 待ち合わせ nowait 指示節を指定すると do ループ終了時の待ち合わせをせず 直ちに次の処理に移ります これにより待ち合わせのオーバーヘッドを減らすことができますが 誤った箇所に nowait を指定すると不正な結果が得られることがあります

ループ内の暗黙の同期 64 二重ループの内側を並列化する場合の 暗黙の同期 マスタースレッド 0 1 2 3!$omp parallel do j = 2, m!$omp do do i = 1, n W(i, j) = W(i, j-1) + Y(i, j)!$omp!$omp end parallel j = 2 j = 3 j = 4 待ち合わせ 待ち合わせ j ループが反復するごとに同期が発生します

ループ内の暗黙の同期の回避 65 二重ループの内側を並列化する場合の 暗黙の同期の回避!$omp parallel do j = 2, m!$omp do do i = 1, n W(i, j) = X(i, j-1) + Y(i, j)!$omp nowait!$omp end parallel 待ち合わせせず 次の j へ進む マスタースレッド 0 j = 2 j = 3 j = 4 1 2 3 j ループの前の反復の計算を全スレッドが完了するのを待つ必要が無い場合のみ nowait を使用できます ループの繰り返し依存がないか注意が必要です j ループの反復数分の同期オーバーヘッドの節約になります

並列実行領域中の逐次処理 66 並列実行領域の中に並列化できない逐次処理を含めたい場合 並列実行領域を終わらせることなく 1 つのスレッドによる逐次処理を行う領域を設けることができます 構文 内容 single 指定された領域の処理を 一つのスレッドが実行します ( マ スタースレッドであるとは限りません ) 指定領域の出口で 暗黙の同期を行います master 指定された領域の処理を マスタースレッドが実行します 指定領域の出口で 暗黙の同期は行いません!$omp parallel!$omp single write(6, * ) Serial processing!$omp end single!$omp end parallel シングル処理一回だけ実行 並列実行領域

バリア同期 (barrier 構文 ) 67 barrier 構文 - スレッドの待ち合わせ ( 同期 ) を行います!$omp parallel!$omp master allocate( V(n) )!$omp end master!$omp barrier!$omp do do i = 1, n V(i) = Y(i)!$omp!$omp end parallel 配列 V の準備 待ち合わせ 暗黙の同期 マスタースレッド 0 1 2 3 待ち合わせ 待ち合わせ master 指示節は 暗黙の同期 ( 待ち合わせ ) を行いませんので スレッド 1 3 の後の処理 ( 緑 ) を待たせるためには barrier 指示節が必要です

critical 構文 ( 排他制御 ) 68 critical 構文は 指定範囲について複数スレッドの処理が重ならないようにし ( 排他制御 ) アクセス競合を回避する real :: sum, xcount, function real :: x(n, m) do j = 1, m!$omp parallel shared ( x, xcount )!$omp single xcount = 0.0!$omp end single!$omp do do i = 1, n!$omp critical if ( x( i, j ).lt. 1.0 ) then xcount = function ( xcount, x( i, j ) ) end if!$omp end critical スレッド 0 1 2 3!$omp do reduction ( +: sum ) do i = 1, 4000 sum = sum + x( i, j ) + xcount!$omp end parallel end 一人ずつアクセスする

その他の同期 排他処理構文 69 構文 atomic flush ordered 内容 critical 指示節と同様に排他処理をしますが こちらの方が高速です ただし インクリメントなどの特定の演算の文にのみ使用できます フラッシュ操作を行います あるスレッドが持つ一時的なビュー ( レジスタやキャッシュ等の内容 ) をメモリの内容と一致させます 指定したループ領域において 逐次実行した場合と同じ順序で実行するよう順序付けを行います

70 実行時ライブラリルーチンと 環境変数

実行時ライブラリルーチン 71 OpenMP では種々のルーチンが用意されている OpenMP API 実行時ライブラリルーチン 並列実行環境の制御や問合せを行う実行環境ルーチン データへのアクセスを同期して行うためのロックルーチン 時間計測ルーチン 利用するにはヘッダファイルを読み込む include omp_lib.h 一般的な Fortran use omp_lib Fortran90モジュールファイル上記の少なくとも一方が存在することになっている

代表的なライブラリルーチン 72 ルーチン名返値内容 omp_get_thread_num integer このルーチンを呼び出したチームに属する スレッド番号を返します 0 [ スレッド数 -1] の値を返します マスタースレッドは0 omp_get_max_threads integer 並列実行領域で利用できるスレッド数の最大値を返 します ( 並列実行領域に入る前でも利用できます ) omp_get_num_threads integer 現在の並列実行領域を実行中のスレッド数を返しま す omp_in_parallel logical 活動状態の並列実行領域内から呼び出された場合.true. それ以外は.false. を返します 並列区間 非並列区間の両方から呼ばれるサブルーチンの分岐に利用できます omp_get_wtime real*8 wall clock の経過時間を秒単位で返します

条件付きコンパイル 73!$ で始まる行は OpenMP でコンパイルする時のみ有効となります OpenMP を使わない場合のエラーを防ぐことができます 以下のプログラムの omp_get_max_threads 等は OpenMP のライブラリ関数なので OpenMP 並列を指定しない場合 リンク時にエラーになります integer :: thrdnum, me thrdnum = omp_get_max_threads() me = omp_get_thread_num() 以下のように!$ を用いて書き換えると 通常の (OpenMP を使用しない ) コンパイルの場合 コメント行と見なされ 互換性を保つことができます integer :: thrdnum, me thrdnum = 1 me = 0!$ thrdnum = omp_get_max_threads()!$ me = omp_get_thread_num() ( プリプロセッサーの構文 #ifdef _OPENMP を用いる方法もあります )

環境変数 74 OpenMP プログラムの実行に影響する主な環境変数 環境変数 OMP_NUM_THREADS OMP_SCHEDULE 内容 並列実行領域で使用するスレッドの数を設定します スケジュールタイプが runtime であるループ指示文のスケジューリングを制御します ( デフォルトは static) OMP_STACKSIZE 各スレッドが実行時に利用するスタックサイズを指定します スレッドごとの固有データなどのメモリ領域に利用されます ( 注 )OMP_STACKSIZE 大きな private 属性の配列を用いるプログラムでは スレッドの private 用のスタックサイズが不足する場合があります そのような場合は この環境変数を十分大きい値で設定します

75 補足 OpenMP 並列化例 OpenMPに限らず並列化の一般知識の補足 OpenMPに関する情報源

76 OpenMP 並列化例 OpenMP による並列化の簡単な例 ここでは OpenMP 並列化の説明を行います 逐次での高速化は チューニング技法 の資料を参考にしてください

77 OpenMP 並列化例 : 個別要素法 / 分子動力学法の例 各粒子にかかる力の計算 入力データ 粒子数 n 粒子の座標 x(n) 簡単のため 1 次元とする 粒子 i,j 間の相互作用 fij 簡単のため 距離に反比例 fij = 1/(x(j) - x(i)) 各粒子にかかる力の計算 粒子 1 にかかる力 f(1) の場合 f(1)= f12 +f13 +f14 +f15 +f16 3 2 f12 f13 1 f14 4 f16 f15 n = 6 6 5 これを f(1) f(6) の全ての粒子について計算図粒子 1 にかかる力 ( 赤矢印 )

78 粒子 i にかかる力の合計を f(i) に保存 f(1) = f 12 +f 13 +f 14 +f 15 +f 16 f(2) = -f 12 +f 23 +f 24 +f 25 +f 26 f(3) = -f 13 -f 23 +f 34 +f 35 +f 36 f(4) = -f 14 -f 24 -f 34 +f 45 +f 46 f(5) = -f 15 -f 25 -f 35 -f 45 +f 56 f(6) = -f 16 -f 26 -f 36 -f 46 -f 56 OpenMP 並列化例 : 個別要素法 / 分子動力学法の例 作用反作用の法則 (fji = fij) より 対称な要素は逆符号で同じ値 3 2 f21= -f12 -f13 f12 f13 1 f14 図作用反作用の法則 (fji = fij) 4 f16 f15 -f14 -f16 n = 6 -f15 6 5

79 OpenMP 並列化例 : 逐次プログラム n: 粒子数 x(n): 粒子の x 座標の配列 f(n): 粒子にかかる力の配列 dimension x(n), f(n) do i=1,n-1 do j=i+1,n fij = 1.d0/(x(j)-x(i)) f(i) = f(i) + fij f(j) = f(j) - fij 粒子 i,j のループ (i<j) fij: 粒子 i への j による力の計算 一つ fij を計算したら 作用 反作用がかかる配列 f の 2 粒子の要素に加算 f(1)= f 12 +f 13 +f 14 +f 15 +f 16 f(2)= -f 12 i=1の演算実行後の f(3)= -f 13 配列 f の状態 f(4)= -f 14 f(5)= -f 15 n = 6 f(6)= -f 16 i=1 最終結果 f(1)= f 12 +f 13 +f 14 +f 15 +f 16 f(2)= -f 12 +f 23 +f 24 +f 25 +f 26 f(3)= -f 13 -f 23 +f 34 +f 35 +f 36 f(4)= -f 14 -f 24 -f 34 +f 45 +f 46 f(5)= -f 15 -f 25 -f 35 -f 45 +f 56 f(6)= -f 16 -f 26 -f 36 -f 46 -f 56 i=1 i=2 i=3 i=4 i=5 図粒子数 n = 6 の場合の計算内容

80 OpenMP 並列化例 : OpenMP 並列化時の検討項目 並列化時の検討項目 (1) 並列化するループの選択 (2) 並列化可能性 ( 計算の順番の依存性 ) の検討 (3) 変数のデータ共有属性の設定 (4) スケジューリングの選択 do i=1,n-1 do j=i+1,n fij = 1.d0/(x(j)-x(i)) f(i) = f(i) + fij f(j) = f(j) - fij

81 OpenMP 並列化例 : (1) 並列化するループの選択 オーバーヘッドの観点から 外側のループの並列化が望ましい!$OMP PARALLEL DO... do i=1,n-1 do j=i+1,n fij = 1.d0/(x(j)-x(i)) f(i) = f(i) + fij f(j) = f(j) - fij 結論 : 外側の i に関するループを並列化したい

82 OpenMP 並列化例 : (2) 並列化可能性の検討 並列化可能性 ( 計算の順番の依存性 ) の検討 i の加算 減算の順番を変えても配列 f は不変 依存性無し ( 丸め誤差は存在する ) f(1)= f 12 +f 13 +f 14 +f 15 +f 16 f(2)= -f 12 +f 23 +f 24 +f 25 +f 26 f(3)= -f 13 -f 23 +f 34 +f 35 +f 36 f(4)= -f 14 -f 24 -f 34 +f 45 +f 46 f(5)= -f 15 -f 25 -f 35 -f 45 +f 56 f(6)= -f 16 -f 26 -f 36 -f 46 -f 56 i=1 i=2 i=3 i=4 i=5 どの i を先に計算しても最終的な配列 f(i) の値は同じ 結論 : 並列化可能

83 OpenMP 並列化例 : (3) 変数のデータ共有属性の設定 スレッド間のアクセス競合が発生する変数を洗い出す 配列 f に関する演算の 2 スレッド実行の例 スレッド0の計算 (i=1 3) スレッド1の計算 (i=4 5) f(1)= f 12 +f 13 +f 14 +f 15 +f 16 f(2)= -f 12 +f 23 +f 24 +f 25 +f 26 f(3)= -f 13 -f 23 +f 34 +f 35 +f 36 f(4)= -f 14 -f 24 -f 34 f(4)= +f 45 +f 46 f(5)= -f 15 -f 25 -f 35 f(5)= -f 45 +f 56 f(6)= -f 16 -f 26 -f 36 f(6)= -f 46 -f 56 i=1 i=2 i=3 i=4 i=5 上の例では スレッド間で要素 f(4) f(6) の同時更新 ( 競合 ) のおそれがあるので スレッドはそれぞれ独自の f を持つ必要がある また 最後に各スレッドの f を要素ごとに合計する必要があるので 配列 f を reduction で指定する必要がある

84 OpenMP 並列化例 : (3) 変数のデータ共有属性の設定 cont. スレッド間のアクセス競合が発生する変数を洗い出す 特に並列ループ内の左辺にある変数に注意する ループ変数 j は自動的に private!$omp PARALLEL DO... REDUCTION(+:f) PRIVATE(fij) do i=1,n-1 do j=i+1,n fij = 1.d0/(x(j)-x(i)) f(i) = f(i) + fij f(j) = f(j) - fij fij の値はスレッドごとに異なる 要 private 化 結論 : 配列 f の reduction 変数化変数 fij の private 化が必要

85 OpenMP 並列化例 : (4) スケジューリングの選択 デフォルトのスケジューリング!$OMP PARALLEL DO REDUCTION(+:f)!$OMP& PRIVATE(fij) do i=1,n-1... チャンクサイズを小さく (1 に ) した例!$OMP PARALLEL DO REDUCTION(+:f)!$OMP& PRIVATE(fij) SCHEDULE(STATIC,1) do i=1,n-1... 6 粒子 2 スレッドでのロードバランス 6 粒子 2 スレッドでのロードバランス f 12 +f 13 +f 14 +f 15 +f 16 f 12 +f 13 +f 14 +f 15 +f 16 -f 12 +f 23 +f 24 +f 25 +f 26 -f 13 -f 23 +f 34 +f 35 +f 36 -f 14 -f 24 -f 34 +f 45 +f 46 -f 15 -f 25 -f 35 -f 45 +f 56 -f 16 -f 26 -f 36 -f 46 -f 56 インバランス大 -f 12 +f 23 +f 24 +f 25 +f 26 -f 13 -f 23 +f 34 +f 35 +f 36 -f 14 -f 24 -f 34 +f 45 +f 46 -f 15 -f 25 -f 35 -f 45 +f 56 -f 16 -f 26 -f 36 -f 46 -f 56 スレッド0 24: 6 スレッド1 スレッド0 18:12 スレッド1 インバランスが緩和 加速率 =30/24 = 1.25 加速率 =30/18= 1.67

OpenMP 並列化例 ( まとめ ) 86 個別要素法 / 分子動力学法の例並列化時の検討項目 (1) 並列化するループの選択外側が望ましい (2) 並列化可能性 ( 計算の順番の依存性 ) の検討並列ループの各サイクルの実行順が 変わっても同じ結果か確認する (3) 変数のデータ共有属性の設定左辺の変数に注意し 必要に応じて PRIVATE や REDUCTION の宣言をする (4) スケジューリングの選択インバランスを緩和するようなスケジューリングを選択する!$OMP PARALLEL DO REDUCTION(+:f)!$OMP& PRIVATE(fij) SCHEDULE(STATIC,1) do i=1,n-1! 並列化するループ do j = i+1, n!! ループ変数は自動的に private となる fij = 1.d0/(x(j)-x(i))! fij の値は i に依存 要 private 化 f(i) = f(i) + fij f(j) = f(j) - fij! ここで j i の要素 f(j) を更新する! 総和の計算 要 reduction 変数化 複数 CPU/ ノードの機種 (cc-numa) の場合 ファーストタッチの検討も必要 ( 本講座では省略 )

並列化率とアムダールの法則 87 アムダールの法則オーバーヘッドを無視した理想的な条件でも速度向上率の上限は逐次部の割合 (1-p) で決まってしまいます 1-p 時間 逐次 逐次部 並列 逐次部 p 並列化済 1/(1-p) 倍が速度向上率の上限 並列化率 80% ( p = 0.8 ) ならば 1/0.2 = 5 倍が上限 予定の並列数にふさわしい並列化率以上である必要があります

並列化率とアムダールの法則 (cont.) 88 アムダール則による速度向上率 逐次部の割合 (1-p) が 1/( 並列数 ) 以下になるよう並列化を進めましょう アムダールの法則 速度向上率 (p: 並列化率,N: 並列数 ) 4.3 2.3 2.9 1.8 逐次部の割合 1-p = 0 ( 並列化率 100%) 1-p = 1/8 1-p = 1/4 1-p = 1/2 (88%) (75%) (50%) 並列数 (N)

情報源 89 OpenMP の仕様情報 http://openmp.org/wp/openmp-specifications/ OpenMP version 3.0 に関しては日本語訳あり 京 のコンパイラ : デフォルトは v3.0 準拠 (v3.1 準拠版も利用可 ) gcc 4.4 以降 :v3.0 準拠 (gcc 4.7 以降 v3.1 準拠 ) intel コンパイラ 11.0 以降 : v3.0 準拠 (12.1 以降 : v3.1 準拠, 13.1(XE 2013 Update 2) 以降 : v4.0 準拠 ) チュートリアル資料もあり ( 上記のページにリンクあり ) https://computing.llnl.gov/tutorials/openmp/ 参考資料 Fortran/C 利用者向け Using OpenMP,B. Chapman 他,The MIT Press Fortran 利用者向け OpenMPによる並列プログラミングと数値計算法, 牛島省, 丸善 C/C++ 利用者向け C/C++ プログラマーのためのOpenMP 並列プログラミング, 菅原清文, カットシステム OpenMP 入門 マルチコアCPU 時代の並列プログラミング, 北山洋幸, 秀和システム 本セミナーの資料は以下のページからダウンロード可 ( 随時更新 ): http://www.hpci-office.jp/pages/seminar_text

90 おまけ Sections 構文特殊なデータ属性

Sections 構文 91 Sections 構文による並列化 処理 A 処理 B 処理 C 処理 D 処理の流れ Single Program, Multiple Data streams (SPMD) 処理 A,B,C,D の中のループが並列化不能でも 処理 A,B,C,D の間に依存性がなく独立に実行できるならば 並列に実行できます!$omp sections!$omp section 処理 A!$omp section 処理 B!$omp section 処理 C!$omp section 処理 D!$omp end sections 処理 A 処理 B 処理 C 処理 D 処理量のバラツキが大きいと並列化の効果が出にくくなります

92 特殊なデータ属性 FirstPrivate/LastPrivate 共有変数がある場合の注意 (Threadprivate 指示文 )

Firstprivate 属性 93 Firstprivate 指示節 - Private 変数と同様の属性を持ちますが 並列実行領域に入る直前の値で 各スレッドの値が初期化されます t = 2.0!$omp parallel do firstprivate(t) do i = 1, 4000 if (i.gt.nmax) t = 0.0 V(i) = X(i) + t * Y(i) プログラム開始 マスタースレッド 0 t= 2.0 V X Y shared(v, X, Y) マスタースレッド 0 t 0 = 2.0 スレッド 1 t 1 = 2.0 スレッド 2 t 2 = 2.0 スレッド 3 t 3 = 2.0 firstprivate( t )

Lastprivate 属性 94 Lastprivate 指示節 -Private 変数と同様の属性をもちますが 並列実行領域後に ループの逐次的な終値に相当する反復後の値が代入されます!$omp parallel do lastprivate(i) do i = 1, 4000 V(i) = X(i) + Y(i) V(i) = X(i)!!! i = 4001 プログラム開始 マスタースレッド 0 V X Y shared(v, X, Y) マスタースレッド 0 スレッド 1 スレッド 2 スレッド 3 i 0 i=1,1000 i 1 i=1001,2000 i 2 i=2001,3000 i 3 i=3001,4000 lastprivate( i ) マスタースレッド 0 i=4001 i=4000 の反復 ( 逐次実行した場合の最終に相当する反復 ) 終了後の値

共有変数がある場合の注意 95 COMMON ブロック /SAVE 変数 Threadprivate 指示文の説明

Threadprivate 指示文 96 以下のとき threadprivate 指示文を使います 複数のルーチンからアクセスする変数 (common ブロック変数 SAVE 変数 module 変数 ) がある かつ その変数が スレッドごとに異なる値を持つ必要がある (=shared 属性では ) ルーチン A 固有なデータ ルーチン B 固有なデータ スレッド 0 ルーチン A threadprivate 属性ルーチン A,B の共通データ 固有なデータ ルーチン B 固有なデータ shared 属性 並列実行領域内のすべてのスレッドからアクセス可能な共有データ スレッド 1 threadprivate 属性ルーチン A,B の共通データ 単に private 属性とすると 新たにスタック領域に変数 配列が確保され 複数のルーチンから共有できなくなってしまいます

Threadprivate 指示文 (cont.) 97 Threadprivate 指示文 (common ブロックの例 ) common ブロック内の変数を スレッド内で複数の subroutine からアクセスできる状態のまま private 化できます ( スレッドごとに固有の値を持つことができます ) 対象とする common ブロックの宣言の直後に記述します 対象が複数ある場合は コンマで区切って記述します 対象が宣言されている全てのプログラム単位 (subroutine 等 ) に記述します common ブロックの要素 equivalence 文に現れる変数は threadprivate 指示文で指定できません 指定された common ブロックの変数は 並列実行領域の終了後も存在し続け 次の並列実行領域でアクセスした時にも データの内容を保持しています common /com/ A, B!$omp threadprivate ( /com/ )

Copyin 指示節と copyprivate 指示節 98 Copyin 指示節 並列領域開始時の threadprivate 変数の初期化!$omp parallel copyin ( /com/ ) マスタースレッド以外のスレッドの threadprivate 変数は 自動的に初期化されません Copyin 指示節により 並列実行領域の開始時にマスタースレッドのデータの内容を各スレッドにコピーします Copyprivate 指示節 Single 領域 ( 並列領域内の逐次領域 ) 終了後の各スレッドへのデータ転送!$omp end single copyprivate ( /com/ ) Single 構文の終りに Single 実行スレッドの変数を 他のスレッドの対応する変数へデータをブロードキャスト ( コピー ) します

ThreadPrivate 指示文の使用例 99 2 つのルーチンが参照する common ブロックを threadprivate 化する例を示します メインルーチン : routine_a サブルーチン : routine_b program routine_a include 'omp_lib.h' common /com/id!$omp threadprivate ( /com/ )!$omp parallel id = omp_get_thread_num() call routine_b!$omp end parallel write(*,*)'2nd parallel region'!$omp parallel call routine_b!$omp end parallel end 並列実行領域 1 並列実行領域 2 subroutine routine_b common /com/id!$omp threadprivate ( /com/ ) write(*,*) id=, id end Threadprivate 変数 id は routine_a, routine_b の両方からアクセス可 変数 id の値はスレッドごとに異なる 2 つ目の並列実行領域でも各スレッドごとの値が保存される

おわり 100 ご清聴ありがとうございました

2018 年 10 月一般財団法人高度情報科学技術研究機構 ( 著作者 ) 本資料を教育目的等で利用いただいて構いません 利用に際しては以下の点に留意いただくとともに 下記のヘルプデスクにお問い合わせ下さい 本資料は 構成 文章 画像などの全てにおいて著作権法上の保護を受けています 本資料の一部あるいは全部について いかなる方法においても無断での転載 複製を禁じます 本資料に記載された内容などは 予告なく変更される場合があります 本資料に起因して使用者に直接または間接的損害が生じても 著作者はいかなる責任も負わないものとします 問い合わせ先 : ヘルプデスク helpdesk[-at-]hpci-office.jp([-at-] を @ にしてください )