第1回 配信講義 計算科学技術特論A(2025)

1.9K Views

April 21, 25

スライド概要

第3回 4 月 24日 OpenMP の基礎
プログラム高速化の基礎知識、並列化プログラミング(MPI、OpenMP)の基礎知識、およびプログラム高速化の応用事例の座学を通して、計算科学で必要な高性能計算技術の基礎の習得を目指す。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

内容に関する質問は [email protected] まで 第3回 OpenMPの基礎 名古屋大学情報基盤センター 片桐孝洋 1 2025年度 計算科学技術特論A

2.

講義日程と内容について  2025年度 計算科学技術特論A(木曜:13:00-14:30 ) 第1回:プログラム高速化の基 礎、2025年4月10日  イントロダクション、ループアンローリング、キャッシュブロック化、 数値計算ライブラリの利用、その他  第2回:MPIの基礎、2025年4月17日  並列処理の基礎、MPIインターフェース、MPI通信の種類、その他  第3回:OpenMPの基礎、2025年4月24日  OpenMPの基礎、利用方法、その他  第4回:Hybrid 並列化技法(MPI 、OpenMP応用とOpenACC)、2025年5月8日  背景、Hybrid並列化の適用事例、利用上の注意、その他  第5回:GPUコンピューティングと大規模学習への展開、2025年5月15日  プログラムの性能ボトルネック に関する考えかた(I/O、単体性能 (演算機ネック、メモリネック)、並列性能(バランス))、性能プロファイル、 機械学習におけるHPC、「富岳NEXT」とGPUコンピューティング、ほか  2 2025年度 計算科学技術特論A

3.

参考書  「計算科学のためのHPC技術1 」 下司雅章 (編集), 片桐孝洋 , 中田真秀, 渡辺宙志, 山 本有作, 吉井範行, Jaewoon Jung, 杉田 有治, 石村和 也, 大石進一, 関根晃太, 森倉悠介, 黒田久泰,著  出版社: 大阪大学出版会 (2017/4/3)  ISBN-10: 4872595866, ISBN-13: 978-4872595864  発売日: 2017/4/3  【本書の特徴】  計算科学に必要なHPC技術について、基礎的な事 項を解説している  片桐担当(1章~5章)  プログラム高速化の基礎、MPIの基礎、OpenMP の基礎、Hybrid並列化技法(MPIとOpenMPの応 用)、プログラム高速化の応用  3 2025年度 計算科学技術特論A

4.

参考書  The Art of High Performance Computing for Computational Science, Vol. 1 Editor: Masaaki Geshi  出版社: Springer, Singapore  Hardcover ISBN 978-981-13-6193-7  発売日: 2019/5  【本書の特徴】  「計算科学のためのHPC技術1」の英語版  片桐担当(1章~5章)  High-Performance Computing Basics(pp. 1-25), Basics of MPI Programming (pp. 27-44), Basics of OpenMP Programming (pp. 45-59), Hybrid Parallelization Techniques (pp. 61-68), Application of Techniques for High-Performance Computing (pp. 69-81)  4 2025年度 計算科学技術特論A

5.

参考書(演習書)  「並列プログラミング入門: サンプルプログラムで学ぶOpenMPとOpenACC」  片桐 孝洋 著  東大出版会、ISBN-10: 4130624563、 ISBN-13: 978-4130624565、発売日: 2015年5月25日  【本書の特徴】  C言語、Fortran90言語で解説  C言語、Fortran90言語の複数のサンプルプログラムが入手可能 (ダウンロード形式)  本講義の内容を全てカバー  Windows PC演習可能(Cygwin利用)。スパコンでも演習可能。  内容は初級。初めて並列プログラミングを学ぶ人向けの 入門書 5 2025年度 計算科学技術特論A

6.

参考書(2024年度出版)  「 並列プログラミングのツボ : 数値計算から機械学習まで」 片桐孝洋 著  出版社: 東京大学出版会 (2024/4/12)  ISBN: 978-4-13-062465-7  【本書の特徴】  スパコンからPCまで、その高速化に並列処理・ 並列プログラミングが重要である。  本書は、その要となる考え方と実装のコツに重 点を置き、必須処理や機械学習を含む様々な 実例を用いて、並列プログラミングの「ツボ」と なる考え方と実装のコツを伝授する。  6 2025年度 計算科学技術特論A

7.

OpenMP 超入門 指示文による簡単並列化 7 2025年度 計算科学技術特論A

8.

OpenMPの概要 8 2025年度 計算科学技術特論A

9.

OpenMPの対象計算機  OpenMPは共有メモリ計算機のためのプログラム言語 OpenMP 実行可能 コード OpenMP 実行可能 コード OpenMP 実行可能 コード OpenMP 実行可能 コード 共有 配列 A[ ] 同時に複数のPEが共有配列にアクセス ⇒並列処理で適切に制御をしないと、逐次計算の結果と一致しない 9 2025年度 計算科学技術特論A

10.

OpenMPとは  OpenMP (OpenMP C and C++ Application Program Interface Version 1.0)とは、共有メモリ型並列計算機用 にプログラムを並列化する以下: 指示文 2. ライブラリ 3. 環境変数 を規格化したものです。 1.   10 ユーザが、並列プログラムの実行させるための指示 を与えるものです。 コンパイラによる自動並列化ではありません。 分散メモリ型並列化(MPIなど)に比べて、データ分散 の処理の手間が無い分、実装が簡単です。 2025年度 計算科学技術特論A

11.

OpenMPとマルチコア計算機(その1)  スレッド並列化を行うプログラミングモデル  近年のマルチコア計算機に適合  経験的な性能: 8~16スレッド程度以下の並列実行に向く  8~16スレッド程度超えるスレッド実行で高い並列化効率を出 すには、プログラミングの工夫が必要 1. 2. 3. メインメモリ‐キャッシュ間のデータ転送能力が演算性能に比べ低い OpenMPで並列性を抽出できないプログラムになっている(後述) ccNUMAの影響(ソケットを超える実行時)  ノード間の並列化はOpenMPではできない  ノード間の並列化はMPIを用いる  自動並列化コンパイラも、スレッド並列化のみ  HPF、 XcalableMP(筑波大、理研AICS) などのコンパイラではノード間の 並列化が可能だが、まだ完全に普及していない 11 2025年度 計算科学技術特論A

12.

OpenMPとマルチコア計算機(その2)  典型的なスレッド数 50~100スレッド/ノード  Fujitsu FX1000 (ARM A64FX)  48物理コア  Intel Xeon Gold 6230×4ソケット (「不老」クラウドシステムの1ノード)  80物理コア  マルチコアCPU:  メニーコアCPU: 60~280スレッド/ノード  Intel Xeon Phi (Intel MIC(Many Integrated Core) 、Knights Landing)  68物理コア、136~272論理コア(HT利用時)  ☞生産終了・・・  2025年現在、256スレッド超のOpenMP実行環境が手元に!  12 性能を出すためには、相当のプログラム上の工夫が必要 2025年度 計算科学技術特論A

13.

OpenMPコードの書き方の原則  C言語の場合  #pragma omp で始まるコメント行  Fortran言語の場合  !$omp で始まるコメント行 13 2025年度 計算科学技術特論A

14.

OpenMPのコンパイルの仕方  逐次コンパイラのコンパイルオプションに、OpenMP用の オプションを付ける 例)富士通Fotran90コンパイラ frt –Kfast,openmp foo.f  例)富士通Cコンパイラ fcc –Kfast,openmp foo.c   注意  OpenMPの指示がないループは逐次実行  コンパイラにより、自動並列化によるスレッド並列化との 併用ができる場合があるが、できない場合もある  OpenMPの指示行がある行はOpenMPによるスレッド並列化、 指示がないところはコンパイラによる自動並列化  例)富士通Fortran90コンパイラ frt –Kfast,parallel,openmp foo.f 14 2025年度 計算科学技術特論A

15.

OpenMPの実行可能ファイルの実行  OpenMPのプログラムをコンパイルして生成した実行可能 ファイルの実行は、そのファイルを指定することで行う  スレッド数を、環境変数OMP_NUM_THREADSで指定  例)OpenMPによる実行可能ファイルがa.outの場合 $ export OMP_NUM_THREADS=16 $ ./a.out  注意  逐次コンパイルのプログラムと、OpenMPによるプログラムの 実行速度が、OMP_NUM_THREADS=1にしても、異なることがある (後述) この原因は、OpenMP化による処理の増加(オーバーヘッド)  高スレッド実行で、このオーバーヘッドによる速度低下が顕著化  プログラミングの工夫で改善可能  15 2025年度 計算科学技術特論A

16.

OpenMPの実行モデル 16 2025年度 計算科学技術特論A

17.

OpenMPの実行モデル(C言語) OpenMP指示文 ブロックA #pragma omp parallel { ブロックB } ブロックC ブロックA スレッドの起動 スレッド0 (マスタースレッド) スレッド1 ブロックB ブロックB ※スレッド数pは、 環境変数 OMP_NUM_THREADS で指定する。 スレッドの終結 ブロックC 17 2025年度 計算科学技術特論A スレッドp … ブロックB

18.

OpenMPの実行モデル(Fortran言語) OpenMP指示文 ブロックA !$omp parallel ブロックB !$omp end parallel ブロックC ブロックA スレッドの起動 スレッド0 (マスタースレッド) スレッド1 ブロックB ブロックB ※スレッド数pは、 環境変数 OMP_NUM_THREADS で指定する。 スレッドの終結 ブロックC 18 2025年度 計算科学技術特論A スレッドp … ブロックB

19.

Work sharing構文  parallel指示文のように、複数のスレッドで実行する場合 において、OpenMPで並列を記載する処理(ブロックB)の 部分を並列領域 (Parallel Region)と呼ぶ。  並列領域を指定して、スレッド間で並列実行する処理を 記述するOpenMPの構文をWork Sharing構文と呼ぶ。  Work Sharing構文は、以下の2種がある。 1. 並列領域内で記載するもの  for構文(do構文)  sections構文  single構文 (master構文)、など 2. parallel指示文と組み合わせるもの  parallel for 構文 (parallel do構文)  parallel sections構文、など 19 2025年度 計算科学技術特論A

20.

代表的な指示文 20 2025年度 計算科学技術特論A

21.

※Fortran言語の場合は For構文(do構文) #pragma omp parallel for for (i=0; i<100; i++){ a[i] = a[i] * b[i]; } !$omp parallel do ~ !$omp end parallel do 上位の処理 スレッドの起動 スレッド0 スレッド1 スレッド2 スレッド3 for (i=0; i<25; i++){ a[i] = a[i] * b[i]; } for (i=25; i<50; i++){ a[i] = a[i] * b[i]; } for (i=50; i<75; i++){ a[i] = a[i] * b[i]; } for (i=75; i<100; i++){ a[i] = a[i] * b[i]; } スレッドの終結 ※指示文を書くループが 並列化をしても、 正しい結果になることを ユーザが保障する。 21 下位の処理 2025年度 計算科学技術特論A

22.

For構文の指定ができない例 for (i=0; i<100; i++) { •ループ並列化指示すると、 逐次と結果が異なる a[i] = a[i] +1; (a[i-1]が更新されていない b[i] = a[i-1]+a[i+1]; 場合がある) } for (i=0; i<100; i++) { a[i] = a[ ind[i] ]; } •ind[i]の内容により、 ループ並列化できるか どうか決まる •a[ind[i]]が既に更新された 値でないとき、 ループ並列化できる 22 計算科学技術特論A 2025年度

23.

※Fortran言語の場合は !$omp parallel sections ~ !$omp end parallel sections Sections構文 #pragma omp parallel sections { スレッド数が3の場合 #pragma omp section sub1(); スレッド0 スレッド1 #pragma omp section sub2(); sub1(); sub2(); #pragma omp section sub3(); sub4(); #pragma omp section sub4(); } スレッド2 sub3(); スレッド数が4の場合 スレッド0 sub1(); 23 スレッド1 スレッド2 sub2(); 2025年度 sub3(); 計算科学技術特論A スレッド3 sub4();

24.

※Fortran言語の場合は !$omp critical ~ !$omp end critical Critical補助構文 #pragma omp critical { s = s + x; } スレッド0 スレッド1 スレッド2 スレッド3 s= s+x s= s+x s= s+x s= s+x 24 2025年度 計算科学技術特論A

25.

Private補助構文 #pragma omp parallel for private(c) for (i=0; i<100; i++){ a[i] = a[i] + c * b[i]; } ただし、c にループに入る前の値を 代入して使う場合は firstprivate(c) スレッド0 for (i=0; i<25; i++){ a[i] = a[i] + c0*b[i]; } ※変数cが各スレッドで 別の変数を確保して実行 →高速化される 上位の処理 スレッド1 スレッドの起動 スレッド2 for (i=25; i<50; i++){ a[i] = a[i] + c1*b[i]; } スレッド3 for (i=50; i<75; i++){ a[i] = a[i] + c2*b[i]; } for (i=75; i<100; i++){ a[i] = a[i] + c3* b[i]; } スレッドの終結 下位の処理 25 2025年度 計算科学技術特論A

26.

Private補助構文の注意(C言語) #pragma omp parallel for private( j ) for (i=0; i<100; i++) { for (j=0; j<100; j++) { a[ i ] = a[ i ] + amat[ i ][ j ]* b[ j ]; } •ループ変数 j が、各スレッドで別の変数を確保して実行される。 •private( j ) がない場合、各スレッドで 共有変数の j のカウントを独立で行ってしまい、逐次と加算結果が異なる。 →演算結果が逐次と異なり、エラーとなる。 26 2025年度 計算科学技術特論A

27.

Private補助構文の注意(Fortran言語) !$omp parallel do private( j ) do i=1, 100 do j=1, 100 a( i ) = a( i ) + amat( i , j ) * b( j ) enddo enddo !$omp end parallel do •ループ変数 j が、各スレッドで別の変数を確保して実行される。 •private( j ) がない場合、各スレッドで 共有変数の j のカウントを独立で行ってしまい、逐次と加算結果が異なる。 →演算結果が逐次と異なり、エラーとなる。 27 2025年度 計算科学技術特論A

28.

リダクション補助構文 (C言語)  内積値など、スレッド並列の結果を足しこみ、1つの結果を 得たい場合に利用する  上記の足しこみはスレッド毎に非同期になされる  reduction補助構文が無いと、ddotは共有変数になるため、 並列実行で逐次の結果と合わなくなくなる #pragma omp parallel for reduction(+: ddot ) for (i=1; i<=100; i++) { ddot += a[ i ] * b[ i ] } ddotの場所はスカラ変数のみ記載可能(配列は記載できません) 28 2025年度 計算科学技術特論A

29.

リダクション補助構文 (Fortran言語)  内積値など、スレッド並列の結果を足しこみ、1つの結果を 得たい場合に利用する  上記の足しこみはスレッド毎に非同期になされる  reduction補助構文が無いと、ddotは共有変数になるため、 並列実行で逐次の結果と合わなくなくなる !$omp parallel do reduction(+: ddot ) do i=1, 100 ddot = ddot + a(i) * b(i) enddo !$omp end parallel do ddotの場所はスカラ変数のみ記載可能(配列は記載できません) 29 2025年度 計算科学技術特論A

30.

reduction補助構文の注意  reduction補助構文は、排他的に加算が行われるので、 一般的に性能が悪い  経験的に、8~16スレッド並列を超える場合、性能劣化が激しい  以下のように、ddot用の配列を確保して逐次で加算する ほうが高速な場合もある(ただし、問題サイズ、ハードウェア依存)  !$omp ) parallel do private ( i ) スレッド数分のループを作成:最大pスレッド利用 do j=0, p-1 各スレッドでアクセスするインデックス範囲を事前に設定 do i=istart( j ), iend( j ) ddot_t( j ) = ddot_t( j ) + a(i) * b(i) 各スレッドで用いる、ローカルなddot用の enddo 配列ddot_t()を確保し、0に初期化しておく enddo !$omp end parallel do ddot = 0.0d0 do j=0, p-1 逐次で足しこみ ddot = ddot + ddot_t( j ) enddo 30 2025年度 計算科学技術特論A

31.

その他、よく使うOpenMPの関数 31 2025年度 計算科学技術特論A

32.

最大スレッド数取得関数  最大スレッド数取得には、omp_get_num_threads()関数 を利用する  型はinteger (Fortran言語)、int (C言語)  Fortran90言語の例  C言語の例 use omp_lib Integer nthreads #include <omp.h> int nthreads; nthreads = omp_get_num_threads() nthreads = omp_get_num_threads(); 32 2025年度 計算科学技術特論A

33.

自スレッド番号取得関数  自スレッド番号取得には、omp_get_thread_num()関数を 利用する  型はinteger (Fortran言語)、int (C言語)  Fortran90言語の例  C言語の例 use omp_lib Integer myid #include <omp.h> int myid; myid = omp_get_thread_num() myid = omp_get_thread_num(); 33 2025年度 計算科学技術特論A

34.

時間計測関数  時間計測には、omp_get_wtime()関数を利用する  型はdouble precision (Fortran言語)、double (C言語)  Fortran90言語の例  C言語の例 use omp_lib double precision dts, dte #include <omp.h> double dts, dte; dts = omp_get_wtime() 対象の処理 dte = omp_get_wtime() print *, “Elapse time [sec.] =”,dte-dts dts = omp_get_wtime(); 対象の処理 dte = omp_get_wtime(); printf(“Elapse time [sec.] = %lf ¥n”, dte-dts); 34 2025年度 計算科学技術特論A

35.

その他の構文 35 2025年度 計算科学技術特論A

36.

※Fortran言語の場合は Single構文 !$omp single ~ !$omp end single  Single補助構文で指定されたブロックを、 どれか1つのスレッドに割り当てる  どのスレッドに割り当てられるかは予測できない  nowait補助構文を入れない限り、同期が入る プログラムの開始 #pragma omp parallel for { ブロックA #pragma omp single { ブロックB } … } 36 スレッドの起動 スレッド0 スレッド1 (マスタースレッド) ブロックA ブロックA ブロックB 同期処理 2025年度 計算科学技術特論A スレッドp … ブロックA

37.

Master構文  使い方は、single補助構文文と同じ  ただし、master補助構文で指定した処理 (先ほどの例の「ブロックB」の処理)は、 必ずマスタースレッドに割り当てる  終了後の同期処理が入らない  そのため、場合により高速化される 37 2025年度 計算科学技術特論A

38.

Flush構文  物理メモリとの一貫性を取る  Flush構文で指定されている変数のみ、その場所で一貫性を取る。 それ以外の共有変数の値は、メモリ上の値との一貫性は無い。 演算結果はレジスタ上に保存されるだけ。 メモリに計算結果を書き込んでいない  つまり、flush補助指定文を書かないと、スレッド間で同時に足しこんだ結果 が、実行ごとに異なる。  barrier補助構文、critical補助構文の出入口、parallel構文の出口、 for、sections、single構文の出口では、暗黙的にflushされている。   Flushを使うと性能は悪くなる。できるだけ用いない。 #pragma omp flush (対象となる変数名の並び) 省略すると、 全ての変数が対象 38 2025年度 計算科学技術特論A

39.
[beta]
Threadprivate構文
スレッドごとにプライベート変数にするが、スレッド内で大域アクセスできる
変数を宣言する。
 スレッドごとに異なる値をもつ大域変数の定義に向く。


たとえば、スレッドごとに異なるループの開始値と終了値の設定
…
#include <omp.h>
void main() {
int myid, nthreds, istart, iend;
…
#pragma omp threadprivate (istart,
#pragma omp parallel private (myid, nthreds,
iend)
istart, iend) {
…
nthreds = omp_num_threds();
void kernel() {
myid = omp_get_thread_num();
int i;
istart = myid * (n/nthreads);
for (i=istart; i<iend; i++) {
iend = (myid+1)*(n/nthreads);
for (j=0; j<n; j++) {
if (myid == (nthreads-1)) {
a[ i ] = a[ i ] + amat[ i ][ j ] * b[ j ];
nend = n;
}
スレッド毎に異なる値を持つ
}
}
大域変数を、Parallel構文中
kernel();
}
で定義する
}
…


39

2025年度

計算科学技術特論A

40.

スケジューリング 40 2025年度 計算科学技術特論A

41.

スケジューリングとは(その1)  Parallel do構文(Parallel for構文)では、対象ループの 範囲(例えば1~nの長さ)を、単純にスレッド個数分に 分割(連続するように分割)して、並列処理をする。 1 スレッド0 スレッド1 スレッド2 スレッド3 スレッド4 n ループ変数の流れ (反復空間)  このとき、各スレッドで担当したループに対する計算負荷 が均等でないと、スレッド実行時の台数効果が悪くなる 1 スレッド0 スレッド1 スレッド2 スレッド3 スレッド4 n 計算負荷 41 2025年度 計算科学技術特論A

42.

スケジューリングとは(その2)  負荷分散を改善するには、割り当て間隔を短くし、かつ、 循環するように割り当てればよい。 1 n 計算負荷  最適な、割り当て間隔(チャンクサイズとよぶ)は、計算機 ハードウェアと、対象となる処理に依存する。  以上の割り当てを行う補助構文が用意されている。 42 2025年度 計算科学技術特論A

43.

ループスケジューリングの補助構文 (その1)  schedule (static, n)  ループ長をチャンクサイズで分割し、スレッド0番から順番に 1 (スレッド0、スレッド1、・・・というように、ラウンドロビン方式と 呼ぶ)、循環するように割り当てる。 nにチャンクサイズを指定できる。  Schedule補助指定文を記載しないときのデフォルトはstaticで、 かつチャンクサイズは、ループ長/スレッド数。 スレッド0 43 スレッド1 スレッド2 スレッド3 2025年度 計算科学技術特論A

44.

ループスケジューリングの補助構文 (その2)  schedule(dynamic, n)  ループ長をチャンクサイズで分割し、処理が終了したスレッド から早い者勝ちで、処理を割り当てる。 nにチャンクサイズを指定できる。 1 スレッド0 44 スレッド1 スレッド2 スレッド3 2025年度 計算科学技術特論A

45.

ループスケジューリングの補助構文 (その3)  schedule(guided, n)  ループ長をチャンクサイズで分割し、徐々にチャンクサイズを 小さくしながら、処理が終了したスレッドから早い者勝ちで、 処理を割り当てる。nにチャンクサイズを指定できる。 チャンクサイズの指定が1の場合、残りの反復処理をスレッド数で割った おおよその値が各チャンクのサイズになる。  チャンクサイズは 1 に向かって指数的に小さくなる。  チャンクサイズに 1より大きい k を指定した場合、チャンク サイズは指数 的に k まで小さくなるが、最後のチャンクは k より小さくなる場合がある。  チャンクサイズが指定されていない場合、デフォルトは 1 になる。  1 スレッド0 45 スレッド1 スレッド2 スレッド3 2025年度 計算科学技術特論A

46.
[beta]
ループスケジューリングの補助構文
の使い方
 Fortran90言語の例
!$omp parallel do private( j ) schedule(dynamic,10)
do i=1, n
do j=indj(i), indj (i+1)-1
y( i ) = amat( j ) * x( indx( j ) )
enddo
enddo
!$omp end parallel do

 C言語の例

46

#pragma omp parallel for private( j ) schedule(dynamic,10)
for (i=0; i<n; i++) {
for ( j=indj(i); j<indj (i+1); j++) {
y[ i ] = amat[ j ] * x[ indx[ j ]];
}
}
2025年度

計算科学技術特論A

47.

ループスケジューリングにおける プログラミング上の注意  dynamic、guidedのチャンクサイズは性能に大きく影響 チャンクサイズが小さすぎると負荷バランスは良くなるが反面、 システムのオーバヘッドが大きくなる。  一方、チャンクサイズが大きすぎと負荷バランスが悪くなる半面、 システムのオーバヘッドが小さくなる。  両者のトレードオフがある。   実行時のチャンクサイズのチューニングが必須で、チューニングコスト が増える。  staticのみで高速実装ができる(場合がある) dynamicなどの実行時スケジューリングは、システムのオーバーヘッド が入るが、staticはオーバーヘッドは(ほとんど)無い。  事前に負荷分散が均衡となるループ範囲を調べた上で、 staticスケジューリングを使うと、最も効率が良い可能性がある。  ただし、プログラミングのコストは増大する  47 2025年度 計算科学技術特論A

48.

Staticスケジューリングのみで負荷バランス を均衡化させる実装例  疎行列‐ベクトル積へ適用した例(詳細は後述) スレッド個数分のループ (スレッドごとのループ担当範囲 を知るために必要) !$omp parallel do private(S, J_PTR,I) DO K=1, NUM_SMP DO I=KBORDER(K-1)+1, KBORDER(K) 事前に調べて設定しておいた、 S=0.0D0 負荷分散が均衡となる DO J_PTR=IRP(I), IRP(I+1)-1 スレッドごとのループ範囲 S=S + VAL( J_PTR ) * X(ICOL( J_PTR)) (各スレッドは、連続しているが、 END DO 不均衡なループ範囲を設定) Y(I)=S END DO 実行前に、各スレッドが担当するループ範囲について、 END DO 連続する割り当てで、かつ、それで負荷が均衡する !$omp end parallel do 問題に適用できる。 ※実行時に負荷が動的に変わっていく場合は適用できない 48 2025年度 計算科学技術特論A

49.

OpenMPのプログラミング上の注意 (全般) 49 2025年度 計算科学技術特論A

50.

OpenMPによるプログラミング上の注意点  OpenMP並列化は、 parallel構文を用いた単純なforループ並列化 が主になることが多い。  複雑なOpenMP並列化はプログラミングコストがかかるので、 OpenMPのプログラミング上の利点が失われる  parallel構文による並列化は private補助構文の正しい使い方 を理解しないと、バグが生じる! 50 2025年度 計算科学技術特論A

51.

Private補助構文に関する注意(その1)  OpenMPでは、対象となる直近のループ変数以外は、 private変数で指定しない限り、全て共有変数になる。  デフォルトの変数は、スレッド間で個別に確保した変数でない  ループ変数に関する共有変数の例 !$omp parallel do 宣言なしにプライベート変数として確保されるのは、 このi-ループ変数のみ do i=1, 100 このj-ループ変数は、private宣言なしでは共有変数になる do j=1, 100 ←スレッド間で早い者勝ちで加算 ←並列実行時にバグ tmp = b(i) + c(i) a( i ) = a( i ) + tmp enddo この変数tmpは、private宣言なしでは共有変数になる ←スレッド間で早い者勝ちで値が代入 ←並列実行時にバグ enddo !$omp end parallel do 51 2025年度 計算科学技術特論A

52.

Private補助構文に関する注意(その2)  Private補助構文に記載する変数を減らすため、 対象部分を関数化し、かつ、その関数の引数を増やすと、 関数呼び出し時間が増加し、スレッド並列化の効果を 相殺することがある  呼び出し関数の引数が多い例 !$omp parallel do do i=1, 100 call foo(i,arg1,arg2,arg3, arg4,arg5, ….., arg100) enddo !$omp end parallel do 52 関数引数は自動的にプライベート変数に なるため、private補助構文に記載する 変数を削減できる ← しかし、関数呼び出し時のオーバーヘッド が増加する ← スレッド実行時においても、関数呼び出し のオーバーヘッドが無視できなくなり、 台数効果が制限される ※解決法:大域変数で引き渡して引数を削減 2025年度 計算科学技術特論A

53.

Private補助構文に関する注意のまとめ  OpenMPでは、宣言せずに利用する変数は、 すべて共有変数(shared variable)になる  C言語の大域変数、Fotran90言語のcommon変数は、 そのままでは共有変数になる  プライベート変数にしたい場合は、Threadprivate宣言が必要  外側ループをParallel構文などで並列化する場合  ループ内で呼ばれる関数(手続き)内で宣言される 変数はPrivateになる  C言語で、ループ内で明示的に宣言される変数 (例:int a;) は Privateになる。 53 2025年度 計算科学技術特論A

54.

Parallel構文の入れ子に関する注意(その1)  Parallel構文は、do構文で分離して記載できる  1ループが対象の場合、分離するとdo構文の場所で ループごとにforkするコードを生成するコンパイラがあり、 速度が低下する場合がある  この逆の場合もある。双方を確認する必要あり。 !$omp parallel !$omp do private(j,tmp) do i=1, 100 do j=1, 100 tmp = b( j ) + c( j ) a( i ) = a( i ) + tmp enddo enddo !$omp end do !$omp end parallel 54 Parallel構文の 対象が1ループ なら parallel do で指定 2025年度 !$omp parallel do private(j,tmp) do i=1, 100 do j=1, 100 tmp = b( j ) + c( j ) a( i ) = a( i ) + tmp enddo enddo !$omp end parallel do 計算科学技術特論A

55.

Parallel構文の入れ子に関する注意(その2)  Parallel構文は、do構文で分離して記載できる  複数ループの内側を並列化したい場合は、分離した 方が高速になる ただし、外側ループを並列化できる時はその方が性能が良い  外側ループにデータ依存があり、並列化できない場合  !$omp parallel do i=1, n !$omp do do j=1, n <並列化できる式> enddo !$omp end do enddo !$omp end parallel do i=1, n !$omp parallel do do j=1, n <並列化できる式> enddo !$omp end parallel do enddo 55 2025年度 計算科学技術特論A

56.

データ依存関係を壊しバグになる例  間接参照があるインデックスに対して加算する例  間接参照のパターン、および、スレッド実行のタイミング次第で、 逐次処理と結果が一致し、正常動作だと勘違いする場合がある   理論的には間違っている OpenMPの共有変数は、データ一貫性の保証はしない  データの一貫性の保証には、critical補助指定文などによる相互排除が必要  バグになるプログラム例 !$omp parallel do private( j ) do i=1, n j = indx( i ) a( j ) = a( j ) + 1 enddo !$omp end parallel do 56 2025年度 !$omp parallel do private( j ) do i=1, n j = indx( i ) !$omp critical a( j ) = a( j ) + 1 !$omp end critical enddo !$omp end parallel do 計算科学技術特論A

57.

Critical補助構文による速度低下  先述のように、critical補助構文を入れないといけない場合、 特に、高スレッド数での実行で性能が低下する  ハードウェアサポートがある場合、atomic節による実装のほうが高速であ ることが多いが、スレッド数が増えると性能が低下する  高性能化するには、基本的にはアルゴリズムを変更するしかない。  この場合、以下の3つのアプローチがある。 スレッド内アクセスのみに限定し、critical補助構文をはずす 1.  スレッド間アクセスを最小化 2.  3. 間接参照されるデータについて、理論的に、割り当てられたスレッド内の データしかアクセスしないように、アルゴリズムを変更する criticalの並列領域に同時に入るスレッド数が減るように、間接参照するデータを 事前に調べ、間接参照するデータの順番を変更する。 スレッド間アクセス部分をループから分離し、逐次処理にする  例)内積演算におけるリダクション補助指定文 57 2025年度 計算科学技術特論A

58.

OpenMPを用いた並列化の欠点 (その1)  OpenMPは単純なループを並列化することに向く  実用アプリケーションにおける複雑なループは、そのままでは OpenMP化に向いていないことがある。 1. private補助構文中に書かれる変数名の数が膨大になる  外側ループからOpenMP並列化する場合、内部で使っている 変数の数が多いことがある  private変数リストに変数を書き忘れても、コンパイラによる エラーは出ない。(並列化の責任はユーザにあるため)  実行すると、タイミングに依存し計算結果が逐次と異なる。 どこが間違っているかわからないので、デバックが大変になる。  解決策:コンパイラによっては、最適化情報を出力することがで きる。その情報から、ちゃんとprivate化されているか確認する。 58 2025年度 計算科学技術特論A

59.

OpenMPを用いた並列化の欠点 (その2) 高スレッド実行時に性能が出ない場合のチューニングが困難  例)8スレッド未満では性能が出るが、8スレッド以上の実行で 性能が劣化する 1. 近年のハードウェアはメモリアクセスの性能が低い 2. ループそのものに並列性がない(ループ長が短い) 3. ccNUMAの影響(ソケットを跨ぐ実行)  解決するには、アルゴリズムの変更、実装の変更、が必要 になり、OpenMPの利点である容易なプログラミングを損なう 3. 複雑なスレッドプログラミングには向かない  単純な数値計算のカーネルループを、parallel for構文で記載 する方針で仕様が作られている(と思われる)  複雑な処理は、PthreadなどのnativeなスレッドAPIで書くほうが やりやすい 2. 59 2025年度 計算科学技術特論A

60.

プログラム実例 60 2025年度 計算科学技術特論A

61.

行列-行列積のコードのOpenMP化の例 (C言語)  以下のようなコードになる #pragma omp parallel for private (j, k) for(i=0; i<n; i++) { for(j=0; j<n; j++) { for(k=0; k<n; k++) { C[i][j] += A[i][k] * B[k][j]; } } } 61 2025年度 計算科学技術特論A

62.

行列-行列積のコードのOpenMP化の例 (Fortran言語)  以下のようなコードになる !$omp parallel do private (j, k) do i=1, n do j=1, n do k=1, n C(i, j) = C(i, j) + A(i, k) * B(k, j) enddo enddo enddo !$omp end parallel do 62 2025年度 計算科学技術特論A

63.

OpenMPの高速化技法: ファーストタッチ 63 2025年度 計算科学技術特論A

64.

ファーストタッチとは  ファーストタッチとは、マルチコア計算機の中でも、 ccNUMA (Cache Coherent Non-Uniform Memory Access) のハードウェア向けの、メモリ最適化の方法  OpenMPによる並列プログラミングでも重要な技法  ccNUMAのメモリ構造の特性を利用する アクセス遅い CPU0 CPU1 メモリ0 メモリ1 メモリ2 メモリ3 CPU2 CPU3 アクセス速い 64 2025年度 ccNUMAの ハードウェア 計算科学技術特論A

65.

ファーストタッチの原理  ccNUMA型のハードウェアでは、確保した配列は、 各コアで、その配列に初めてアクセスした時、 各コアに最も近いメモリに配列が置かれる  この原理を利用し、本計算と同じデータ・アクセスパターン (=ループ構造)で、プログラム上最も先に、 OpenMP指示文を用いて配列を初期化すると、 CPUに近いメモリに配列データがセットされる  本計算と同じループ構造で、確保した配列の初期化 (例えば0クリア、もしくが、データのセット)をするだけで、 ファーストタッチが実現できる 65 2025年度 計算科学技術特論A

66.

ファーストタッチの例 (C言語の例) #pragma omp parallel for private( j ) for (i=0; i<100; i++) { for (j=0; j<100; j++) { a[ i ] = 0.0; amat[ i ][ j ] = 0.0; } …. #pragma omp parallel for private( j ) for (i=0; i<100; i++) { for (j=0; j<100; j++) { a[ i ] = a[ i ] + amat[ i ][ j ]* b[ j ]; } 66 2025年度 ファーストタッチの ための初期化 (プログラムの 一番最初に 実行すること) ファーストタッチ データを利用した 本計算 計算科学技術特論A

67.

ファーストタッチの例 (Fortran言語の例) !$omp parallel do private( j ) ファーストタッチの do i=1, 100 ための初期化 do j=1, 100 (プログラムの a( i ) = 0.0d0 一番最初に amat( i , j ) =0.0d0 enddo 実行すること) enddo !$omp end parallel do …. !$omp parallel do private( j ) do i=1, 100 ファーストタッチ do j=1, 100 a( i ) = a( i ) + amat( i , j ) * b( j ) データを利用した enddo 本計算 enddo !$omp end parallel do 67 2025年度 計算科学技術特論A

68.

ファーストタッチの効果  うまくいく場合で、経験的に約2~5倍高速化  効果的な例  キャッシュに載るようなサイズやアクセス領域 での実行時  必ずしも、効果があるとは限らない  キャッシュミスヒットが大きい場合  そもそも、ファーストタッチの実装が困難 ☞次のスライド 68 2025年度 計算科学技術特論A

69.

ファーストタッチの実装上の注意  ccNUMAのアーキテクチャでないと効果がない  スーパーコンピュータ「富岳」、スーパーコンピュータ「不老」では、 ハードウェア的に効果が期待できる  ただし、今後のいかなるCPUでもccNUMAアーキテクチャになる  対象となる配列を自ら確保し、演算も自ら行う 「手製の」プログラムでないと効果がない  数値計算ライブラリを使う場合  「配列データ」はライブラリを使うユーザが用意する。  一般的に、配列データの値(行列の要素値)を設定するプログラムが先に 動いて、その後、数値計算ライブラリを呼ぶ  このとき、数値計算ライブラリ内でのアクセスパターンがわからないので、 配列データを設定するプログラムのアクセスパターンが数値計算ライブラリ 内のデータアクセスパターンと異なる  以上の理由から、ファーストタッチできない 69 2025年度 計算科学技術特論A

70.

task構文 70 2025年度 計算科学技術特論A

71.

task構文 (OpenMP 3.1)  今までは、スレッド並列で実行  全ての実行は、対象実行の前に並列性(スレッド数)を OMP_NUM_THREADSで設定  タスク構文  タスク並列で行う  対象の場所の並列性を柔軟に増やしたり、 実行するコア割り当てを実行時に決めることがで きる ⇒記述の柔軟性が高い  欠点:タスク生成コストが高く、実行時間で スレッド並列に劣ることがある 71 2025年度 計算科学技術特論A

72.

task構文の例 !$omp parallel num_threads(3) !$omp single !$omp task タスクA !$omp end task !$omp task タスクB !$omp end task !$omp task タスクC !$omp end task !$omp end single !$omp end parallel 72 スレッドの起動 スレッド0 スレッド1 スレッド2 タスクA生成 タスクB生成 タスクA実行 タスクC生成 タスクC実行 2025年度 計算科学技術特論A タスクB実行

73.

OpenMP 4.0 73 2025年度 計算科学技術特論A

74.

OpenMP 4.0  2013年7月仕様公開  http://www.openmp.org/mp-documents/OpenMP4.0.0.pdf  デバイス(GPU等)へのOpenMP演算のオフロード指定  Target構文  複数の並列デバイスを指定  Terms構文  SIMD指定  SIMD構文  スレッドとコアへの割り当て指定(NUMAアフィニティ)  Proc_bind節  GPU利用について、後述のOpenACCと同等の機能 74 2025年度 計算科学技術特論A

75.

OpenMP 5.0  2018年11月仕様公開  https://www.openmp.org/wp-content/uploads/OpenMP-API- Specification-5.0.pdf  OpenMP4.5との差分 メモリモデル:指定されたフラッシュプロパティの扱い  実装固有の機能を必要とするアプリケーションをサポート:requiresディレク ティブ  オフロード強化:デバイス構造の実行のランタイム制御をサポートする構文  配列シェーピングとCおよびC++における非ユニットストライドの配列セク ションのサポート追加  タスク構造にアフィニティ句が追加。明示的なタスクのデータアフィニティを 示すヒントをサポート など  75 2025年度 計算科学技術特論A

76.

OpenMP 6.0  2024年11月仕様公開  https://www.openmp.org/wp-content/uploads/OpenMP-API- Specification-6-0.pdf  OpenMP5.0との差分 forループ内の処理の並列化をより柔軟に行えるようにする新たな句  複数スレッド間のデータの共有をより効率的に行える機能追加  C/C++20、Fortran 2023 との親和性向上  コンパイラ最適化のヒント(ループ変換)が loop 構文で 可能(unroll, tile, collapse, bind, reorder)  offloading のさらなる強化: 複数GPU・複雑なメモリ階層への対応強化 など  76 2025年度 計算科学技術特論A

77.

OpenACCへの展開 77 2025年度 計算科学技術特論A

78.

OpenACCへの展開  GPUを、OpenMPのように、ディレクティブで指定して使う OpenACCが普及しつつある。 2025年現在、従来よりGPUでの実行性能が上がってきた  OpenMP 4.0でもGPUを扱える。どちらが普及するかわからない。   GPUメーカの戦略で縛られている  OpenMP化されたプログラムは、比較的簡単に、OpenACCに変 換できる  OpenMP の Parallel構文 → OpenACC の Kernel構文 か Parallel構文 に書き換え  注意する点は: CPU→GPU、および、GPU→CPUのデータ移動の最小化  データ転送の対象となる配列を指定するData構文が重要  78 2025年度 計算科学技術特論A

79.

Data構文の節 !$acc data … !$acc end data GPU copyin A データの転送 A 結果の書戻し create present copyout CPUメモリ 79 デバイスメモリ 2025年度 計算科学技術特論A

80.

do iter = 1, MAX_ITER !$acc kernels do i=1, n do j=1, n b(i) = A(i, j) * … enddo enddo !$acc end kernels … !$acc kernels do i=1, n do j=1, n b(i) = b(i) + A(i, j) * … enddo enddo !$acc end kernels … enddo 80 CPUメモリ デバイスメモリ A(i, j) データの転送 A(i, j) b(i) 結果の書戻し b(i) CPUメモリ デバイスメモリ A(i, j) A(i, j) b(i) b(i) 2025年度 データの転送 結果の書戻し 計算科学技術特論A b(i) b(i)

81.

!$acc data copyin(A) create(b) do iter = 1, MAX_ITER !$acc data present(A, b) !$acc kernels do i=1, n do j=1, n b(i) = A(i, j) * … enddo enddo !$acc end kernels !$acc end data … !$acc data present(A, b) !$acc kernels do i=1, n do j=1, n b(i) = b(i) + A(i, j) * … enddo enddo !$acc end kernels !$acc end data … enddo !$acc81end data CPUメモリ デバイスメモリ A(i, j) データの転送 A(i, j) b(i) デバイスメモリ A(i, j) b(i) デバイスメモリ上にあるデータのみで演算 (CPUメモリからの転送、および、 CPUメモリへの書き戻しが無い) 2025年度 計算科学技術特論A

82.

レポート課題(その1)  問題レベルを以下に設定 問題のレベルに関する記述: •L00: きわめて簡単な問題。 •L10: ちょっと考えればわかる問題。 •L20: 標準的な問題。 •L30: 数時間程度必要とする問題。 •L40: 数週間程度必要とする問題。複雑な実装を必要とする。 •L50: 数か月程度必要とする問題。未解決問題を含む。 ※L40以上は、論文を出版するに値する問題。  教科書のサンプルプログラムは以下が利用可能 (ただし、MPIの部分をコメントアウトする必要あり)  Mat-Mat-noopt-fx.tar  Mat-Vec-fx.tar 82 2025年度 計算科学技術特論A

83.

レポート課題(その2) 1. 2. 3. [L10] 行列‐行列積のコードをOpenMPで並列化せよ。 また、1スレッド実行に対する台数効果を測定せよ。 [L10] 行列‐行列積のコードについて、ファーストタッチを 実装し、性能を評価せよ。 [L20]疎行列‐行列積のコードについて、OpenMPで並列化 せよ。また、1スレッド実行に対する台数効果を測定せよ。 83 2025年度 計算科学技術特論A

84.

レポート課題(その3) 4. 5. 6. 7. [L10] データスコープ属性とは何か調べよ。また、 firstprivate, lastprivate補助構文の機能は何かを調べよ。 [L10] Barrier指示文、Nowait補助構文について調べよ。ま たどのように利用するか例を記載して説明せよ。 [L10] 本講義で取り上げていない、OpenMPの 実行時ライブラリ関数を調べ、その機能と利用方法を記せ。 [L10] OMP_NUM_THREADS以外のOpenMPで定義された 環境変数を調べ、その機能を説明せよ。 84 2025年度 計算科学技術特論A

85.

レポート課題(その4) [L10] スケジューラの補助指示構文runtimeの機能調べよ。 また、OpenMPの環境変数との関係を説明せよ。 9. [L15] OpenMP version 3.0、もしくは、4.0の仕様を調べよ。 10. [L15] OpenACC version 1.0、もしくは2.0 の仕様を調べよ。 11. [L10~] 自分の持っている逐次コードを、OpenMPで並列化 せよ。スレッド数を変化させて、台数効果を調べよ。 8. 85 2025年度 計算科学技術特論A