openmp編) - hpci-office.jp · 登録施設利用促進機関 / 文科省委託事業「 ......

Post on 18-Oct-2019

3 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

高度情報科学技術研究機構

1登録施設利用促進機関 / 文科省委託事業「HPCIの運営」代表機関一般財団法人 高度情報科学技術研究機構(RIST)

RIST主催の講習会等2

HPCプログラミングセミナー初心者向け

汎用的な計算機: チューニング・並列化(OpenMP・MPI) 特定の計算機 (アーキテクチャ): メニーコア・アクセラレータ

ワークショップ等経験者向け: ユーザー間の情報共有

RIST主催・共催の講習会・セミナー・ワークショップ一覧http://www.hpci-office.jp/pages/seminars

RIST 講習会 で出てきます。多分。

Outline3

はじめに(現在のHPCについて)

並列処理

OpenMP入門 並列実行領域中のデータの属性

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

ループの並列化と依存性

Reduction演算

DOループのスケジューリング

並列の制御・同時処理

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

OpenMP並列化例

はじめに

現在のHPC用コンピュータの形態 複数のコンピュータ群からなる

1台1台をノードと呼ぶ 性能を活かすにはノード間の並列化が必要 参考:「京」は約8万ノード

マルチコアCPU(メニーコア) CPU内に複数のプロセッサーコア パソコンでも主流 性能を活かすにはノード内の並列化が必要 参考:「京」は8コアCPUが1個/ノード

アクセラレータ(省略)Graphics Processing Unit(GPU)

本日の題目ノード内並列化に多く用いられるOpenMPの入門的内容(ノード間並列化に使われるMPIはこの後)

ネットワーク(インターコネクト)

4

ノード

CPU メモリ

ノード

CPU メモリ

ノード

CPU メモリ

ノード

CPU メモリ

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

Core

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

並列処理5

並列処理とは6

処理を分割して同時並列に実行すること

処理終了までの時間の短縮が目的

マルチコア環境では各コアに処理を分配したい。

逐次

時間

処理

処理

処理

処理

4並列

処理 処理 処理 処理

プロセスとスレッド7

スレッド プロセスより小さい実行単位(処理の分割単位)

1プロセス内のスレッドはメモリ空間を共有する。 1つのスレッドは1つのコアで実行される。

逐次実行中(通常)のプロセス(シングルスレッド)

メモリ空間

スレッド

プロセス

メモリ空間

並列実行中のプロセス(2スレッド)

プロセス

スレッド スレッド

プロセスとスレッド(cont.)8

スレッド プロセスより小さい実行単位(処理の分割単位)

マルチスレッドのプロセスはマルチコアの性能を引き出せる。

1スレッドプロセスを処理中の2コアCPU

2スレッドプロセスを処理中の2コアCPU

稼働中 空き

スレッド

稼働中 稼働中

スレッド スレッド

CPU CPU

コア コア コア コア

おもな並列化方式9

プロセス並列スレッド並列

メモリ空間

メモリ空間

プロセス プロセス

プロセス間通信

メモリ空間

スレッド スレッド

ノード間の並列(分散メモリ並列、ノード内の並列も可)

ノード内の並列(共有メモリ並列)

MPI(Message Passing Interface)OpenMP・自動並列

プロセス

スレッド スレッド

おもな並列化方式(cont.)10

ハイブリッド並列(OpenMP+MPI)

プロセス間通信

メモリ空間

スレッド スレッド

プロセス

例えば、「京」コンピュータでは、通信量の削減の観点から、ノード内をOpenMP並列または自動並列、ノード間をMPI並列、という両者を組み合わせたハイブリッド並列が推奨されている。

メモリ空間

スレッド スレッド

プロセス

ノード ノード

OpenMPのHello worldプログラムと

DOループの並列化を紹介します。

OpenMP入門11

OpenMPによる並列化12

OpenMP構文による並列化

do i = 1, 4000A(i) = B(i) + C(i)

end do

!$omp parallel dodo i = 1, 4000

A(i) = B(i) + C(i)end do

!$omp end parallel do

ループを挟むように構文(ディレクティブ)を挿入

代表的な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

program hellowrite(*,*) ‘Hello world’

!$omp parallelwrite(*,*) ‘Hello OpenMP world’

!$omp end parallelend

●parallel構文

!$omp parallel [指示節 [,指示節]]

parallel~end parallelで囲まれた領域を並列実行します。

並列実行領域(parallel region)

実行例:Hello OpenMP world16

$

並列実行領域の出力(4並列実行)

(*) intel fortran: ifort –openmp hello.fGNU: gfortran –fopenmp hello.f「京」 login-node: frtpx –Kopenmp hello.f

(**) cshの場合: setenv OMP_NUM_THREADS 4

Hello worldHello OpenMP worldHello OpenMP worldHello OpenMP worldHello OpenMP world$

← OpenMPオプションをつけてコンパイルfrt -Kopenmp hello.f (*)

← スレッド数(並列数)を環境変数で設定

$ export OMP_NUM_THREADS=4 (**)

← 実行$ ./a.out← 逐次部分からの出力

コンパイラによってオプションが違う

OpenMP スレッドの動作17

!$omp parallel

並列実行領域

逐次実行領域

!$omp end parallel

逐次実行領域

プログラム開始

マスタースレッド

分岐(fork): スレッドチーム結成(ワーカースレッド生成)

スレッド0=マスタースレッド

スレッド2 スレッド3スレッド1

合流(join): スレッドチーム消滅(ワーカースレッド消滅)

プログラム終了

マスタースレッド

パラレル構文の効果18

Parallel構文 スレッドの分岐・合流を制御

処理の割り振りはしない。

並列化には

処理の割り振り、が必要

後述のワークシェアリング構文

を利用する。

逐次

処理

処理

処理

処理

処理

処理

処理

処理

時間

並列?

ワークシェアリング構文19

Parallel構文は、スレッドチームの分岐/合流を制御する。並列化には、さらに、処理の割り振り(ワークシェアリング)が必要

→ OpenMPではワークシェアリング構文を用いる。

ワークシェアリング構文の種類 ループ構文

doループを分割実行

single構文 生成されたスレッドのうち1つのスレッドのみが実行

sections構文 依存関係のない異なる処理をそれぞれのスレッドで実行

workshare構文(Fortranのみ) fortran90以降の配列代入文などを分割実行

本資料では、ループ構文を主に扱います。

DOループのワークシェアリング20

●do構文(ループ構文)

・並列実行領域において、doループを分割し、チーム内の各スレッドに割り当てます。

・デフォルトでは、均等に分割され、各スレッドにより実行されます。

do i = 1, 4000V(i) = X(i) + Y(i)

end do

ループ長 n=4000

n=40001 2 3

逐次

1000 2000 3000 4000

スレッド0が実行 スレッド1が実行 スレッド2が実行 スレッド3が実行4並列

4並列で実行

!$omp parallel!$omp do

!$omp end do!$omp end parallel

DOループのワークシェアリングの書式(Fortran)

21

do i = 1, 4000V(i) = X(i) + Y(i)

end doこのDOループを並列化する

●パラレルループ構文(パラレル構文とループ構文の複合)

!$omp parallel do [指示節 [,指示節]]

doループ

[!$omp end parallel do] (省略可能)

・後続のdoループを各スレッドで分割して並列実行します。

!$omp parallel dodo i = 1, 4000

V(i) = X(i) + Y(i) end do

!$omp end parallel do

!$omp parallel!$omp do

do i = 1, 4000V(i) = X(i) + Y(i)

end do!$omp end do!$omp end parallel

●パラレル構文!$omp parallel [指示節 [,指示節]]

並列実行領域!$omp end parallel (省略不可)parallel~end parallelで囲まれた領域を並列実行

●ループ構文(DO構文)!$omp do [指示節 [,指示節]]

doループ[!$omp end do] (省略可能)

DOループのワークシェアリングの書式(Fortran)の省略形

22

do i = 1, 4000V(i) = X(i) + Y(i)

end doこのDOループを並列化する

!$omp parallel dodo i = 1, 4000

V(i) = X(i) + Y(i) end do

!$omp end parallel do

!$omp parallel!$omp do

do i = 1, 4000V(i) = X(i) + Y(i)

end do!$omp end do!$omp end parallel

!$omp parallel dodo i = 1, 4000

V(i) = X(i) + Y(i) end do

!$omp end parallel doを省略すると

!$omp parallel!$omp do

do i = 1, 4000V(i) = X(i) + Y(i)

end do!$omp end parallel

!$omp end doは省略可能です。

Forループのワークシェアリングの書式(C言語)

23

for ( i=0 ; i<4000 ; i++ ) {V[i] = X[i] + Y[i];

}このforループを並列化する

●パラレルループ構文(C言語)

#pragma omp parallel for [指示節 [,指示節]]

後続のforループを各スレッドで分割して並列実行します。

#pragma omp parallel forfor ( i=0 ; i<4000 ; i++ ) {

V[i] = X[i] + Y[i]; }

#pragma omp parallel{

#pragma omp forfor ( i=0 ; i<4000 ; i++ ) {

V[i] = X[i] + Y[i]; }

}

●ループ構文(for構文)(C言語)

#pragma omp for [指示節 [,指示節]]

後続のforループを分割して各スレッドに割り当てます。

●パラレル構文(C言語)

#pragma omp parallel [指示節 [,指示節]]

後続の領域を並列実行します。

スレッド間でのデータの共有属性

(shared属性とprivate属性)

並列実行領域中のデータの属性24

データ共有属性(並列領域内の変数・配列の属性) Sharedデータ: 全てのスレッドからアクセス可能なデータ

Privateデータ: 各スレッド固有の、他のスレッドからは見えないデータ

OpenMPでは、データ共有属性をプログラマの責任で設定する必要があります。 誤った設定は、不正な結果(バグ)の原因となります。

並列実行領域中のデータの属性25

!$omp parallel dodo i = 1, 4000

V(i) = X(i) + Y(i) end do

privateデータ

privateデータ

sharedデータ

V(:), X(:), Y(:)i i

スレッド スレッド

データ共有属性の宣言 parallel構文やdo構文の指示節として指定します。

暗黙のデータ共有属性 並列実行領域において、指定の無いほとんどのデータは、デフォルト

で shared属性(詳細は後述)

parallel doまたはdo構文のループ内のループインデックス変数は、その構文内で private属性

暗黙のデータ共有属性により、上記の例では、

「private(i) shared(V, X, Y)」を省略可

データ共有属性の宣言26

!$omp parallel do private(i) shared(V, X, Y)do i = 1, 4000

V(i) = X(i) + Y(i)end do

Shared属性27

プログラム開始

マスタースレッド0

マスタースレッド0 スレッド3スレッド1 スレッド2

V X Yshared(V, X, Y)

並列実行領域

Sharedデータ すべてのスレッドから参照可能。

並列実行領域開始前と同一の(メモリ領域に記憶される)変数

shared指示節で指定されたデータ、あるいは暗黙のshared属性データ

Private属性28

プログラム開始

マスタースレッド0

マスタースレッド0 スレッド3スレッド1 スレッド2

V X Y shared(V, X, Y)i

private( i )i0 i1 i2 i3

互いにアクセスすることはできない

Privateデータ 各スレッドに固有のデータ。他のスレッドからはアクセス不可。

並列実行領域前の対応する変数とは別の実体(メモリー領域)を持つ。

初期値は未定義。

Private指示節で指定されたデータ

※ 例えば i0はスレッド0に固有の i を表す。

暗黙のデータ共有属性(Fortran)29

・・・!$omp parallel!$omp do

do i=1,4X(i)=icall sub(X(i))

end do!$omp end do!$omp end parallel

・・・

subroutine sub(y)common /com/ nsave ymaxreal :: a = 1.0real :: b

・・・

end

並列実行領域内

Private: ループ構文内のループインデックス ( i )他にParallel構文内の逐次DOループインデックス、

DO型反復のインデックスもprivate

Shared: 何の指定もない変数 ( X(:) ) default指示節により変更可(暗黙のデータ共有属性)

default(shared), default(private), default(none)

「none」の場合は明示的に指定しなければならない

コールされたルーチン内

仮引数はcall元のルーチンに従う

yのアドレスはcall元のX(i)を指しているので、その設定に従う。

Shared: COMMON/SAVE文の変数 (n, ymax, a)他にmoduleやsave属性の変数もshared

Private: 上記以外の変数 (b)

※詳しくは仕様書を参照してください。a = 1.0 のように初期値を与えると save属性

暗黙のデータ共有属性(Fortran)並列実行領域内

30

・・・!$omp parallel!$omp do

do i=1,4X(i)=icall sub(X(i))

end do!$omp end do!$omp end parallel

・・・

ループ構文内のループインデックス( i )はPRIVATE。他に、parallel構文内の逐次のdoループインデックスや、do型反復の

インデックスも、PRIVATE。

上記以外の何の指定もない変数 ( X(:) )は、SHARED。この「暗黙のデータ共有属性」は、default指示節により変更可能。default(shared), default(private), default(none)「none」の場合は、明示的に指定しないとエラーになる。

暗黙のデータ共有属性(Fortran)コールされたルーチン内

31

subroutine sub(y)common /com/ nsave ymaxreal :: a = 1.0real :: 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, 4t = X(i) + Y(i)V(i) = V(i) + t * t

end do

スレッド0

同時更新

・t をprivate属性指定しないと、t はshared属性なので、 t はそれぞれのスレッドから同時更新され、タイミングによって結果が異なってしまいます。

⇒ !$omp parallel do private( t )

t

スレッド1

t = X(1) + Y(1)V(1) = V(1) + t * t

t = X(2) + Y(2)V(2) = V(2) + t * t

t = X(3) + Y(3)V(3) = V(3) + t * t

t = X(4) + Y(4)V(4) = V(4) + t * t

一時変数を含むループ(cont.)34

●private属性指定の見落とし

-右の例ではループインデックス i 以外、

すべてshared属性となってしまう。

shared(t, V, X, Y)

V X Yt

↓t

!$omp parallel dodo i = 1, 4000

t = X(i) + Y(i)V(i) = V(i) + t * t

end doマスタースレッド0

スレッド0が更新t =…

t * tスレッド0が結果を読込

スレッド1が更新

スレッド1が結果を読込

プログラマが期待?した動作

各スレッドの更新→読込に、運良く重なりが無ければ正しい結果となる。

一時変数を含むループ(cont.)35

●private属性指定の見落とし

-右の例ではループインデックス i 以外、

すべてshared属性となってしまう。

shared(t, V, X, Y)

!$omp parallel dodo i = 1, 4000

t = X(i) + Y(i)V(i) = V(i) + t * t

end doマスタースレッド0

スレッド0が更新

別スレッドの意図せぬ更新

スレッド0が不正な結果を読込

異常終了せず、常に不正な値を与えるわけではないので、表面化しにくい。

意図しないタイミングでのtの更新が発生する可能性があり、時々不正な結果となる。

t =…

t * t

V X Yt

↓t

一時変数を含むループ(不正の回避)36

プログラム開始

マスタースレッド0

private( t )

shared(V, X, Y)

!$omp parallel do private( t )do i = 1, 4000

t = X(i) + Y(i)V(i) = V(i) + t * t

end do

●private属性を正しく指定する

-左辺にあって複数のスレッドが更新する変数はprivateにする。

V X Y

マスタースレッド0

t0

スレッド1

t1

スレッド2

t2

スレッド3

t3

プログラムは正常に動作します。

サブルーチンコールのあるループ37

……!$omp parallel do

do i = 1, 4call SUB( 〜 )

end do……

スレッド0 スレッド1

Subroutine SUB( 〜 )……call SUB2( 〜 )……end

Subroutine SUB2( 〜 )COMMON /COM/ WORK(100)WORK(1) = 〜……〜 = WORK(1)……end

WORK(1) = 〜……

〜 = WORK(1)

WORK(1) = 〜……

〜 = WORK(1)同時更新の

恐れあり

WORK(1)サブルーチン内部のcommonやsave属性の変数に注意してください。

上記の例では、他のサブルーチンで/COM/を利用している場合は、threadprivate構文でprivate化します。(詳細はおまけの「共有変数がある場合の注意」を参照)

サブルーチン内でもcommonの変数はshared

スレッドセーフとは(競合と排他制御)38

スレッドセーフなルーチン 複数のスレッドで同時に実行しても意図した機能を果たすルーチン

例えば、間接参照を含むルーチンで同一のアドレスをアクセスしてしまうような処理は、競合が発生しており、スレッドセーフではなくなります。

スレッドセーフでない処理の例: COMMONやSAVE属性の変数にアクセスしている関数・サブルーチンで、排他制御が正しく

できていないルーチン

ローカル変数であっても、コンパイル時に-saveオプションを付加した場合

同一のユニット番号に対するREADやWRITEなどのI/O処理(Fortranの規格ではスレッドセーフを保証していません。) 本資料では理解しやすさのため、並列実行領域内でwrite文を実行するプログラム例が多数ありま

すが、本来はcritical構文等で排他制御をすべきです。

スレッドセーフでない処理を並列実行してしまうと、 計算結果が不正だったり、プログラムが異常終了する場合があります。

常に発生するとは限らない上、実行環境により頻度が変わるため、問題が発見しにくい場合があります。

PRIVATE/SHARED属性宣言のミス(アクセス競合)

不正な結果(バグ)の原因 実行の度に結果が異なる場合がある。

発生頻度は、2回に1度のこともあれば、数千回に1回のことも → 発見しにくい

OpenMPではデータ共有属性を注意して設定する必要がある。

コンパイラによっては警告を出してくれることもある。

OpenMP化によるミスの特徴39

並列化してはダメなループ

ループの並列化と依存性40

並列化できないループ・並列化してはダメなループ

41

ループ構文で並列化できないループ: exit等、途中でループを抜ける命令があるループ。

goto~continueのループ構造、等 コンパイルできない。

並列化してはいけないループ(次スライドで説明):他のサイクルの結果を参照するループ

=反復(サイクル)間に依存性のあるループ

!$omp parallel dodo i = 1, N

if ( 〜 ) exitA(i) = A(i) + 1.0

end do

!$omp parallel dodo i= 2, N

V(i) = V(i - 1) + aend do

OpenMPで“並列”実行できてしまうが、逐次実行と異なる結果を与えてしまう。

並列化してはいけない例(1)フロー依存のあるループ(逐次)

42

do i = 2, 7V(i) = V(i - 1) + a

end do

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

逐次実行

上のサイクルの結果を利用して計算している。

例えば、i=5の計算:

V(5)は、サイクルi=4の結果のV(4)を用いて計算する。

分割

※フロー依存とは、Write After Read

並列化してはいけない例(1)フロー依存のあるループ

43

!$omp parallel dodo i = 2, 7

V(i) = V(i-1) + a end do

スレッド0 スレッド1

計算結果が実行順序に

依存

(注)スレッド0がV(4)を計算する前に、スレッド1がV(4)を参照してしまいます。

V(2) = V(1) + a

V(3) = V(2) + a

V(4) = V(3) + a最後にV(4)の計算が完了

V(5) = V(4) + a

V(6) = V(5) + a

V(7) = V(6) + a

最初に更新後のV(4)の値が必要

!$omp parallel do private(t)

do i = 2, 7t = V( i – 1 ) V( i ) = t + a

end do

一時変数を使っても同じ

並列化してはいけない例(2)逆依存のあるループ(逐次)

44

do i = 1, 6V(i) = V(i+1) + a

end do

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

逐次実行

上のサイクルで使用した要素を上書きしながら計算している。

例えば、i=3の計算: V(3)は、更新前のV(4)を用い

て計算し、次のi=4で、V(4)は上書きされます。

分割

※逆依存とは、Read After Write

並列化してはいけない例(2)逆依存のあるループ

45

!$omp parallel dodo i = 1, 6

V(i) = V(i+1) + a end do

スレッド0 スレッド1

計算結果が実行順序に

依存

(注)スレッド0がV(4)の元の値を参照とする前に、スレッド1が先にV(4)の値を更新してしまいます。

最後に更新前のV(4)が必要

V(1) = V(2) + a

V(2) = V(3) + a

V(3) = V(4) + a

V(4) = V(5) + a

V(5) = V(6) + a

V(6) = V(7) + a

最初に V(4)の値を上書き

スレッド1

!$omp parallel do private(t)

do i = 1, 6t = V( i +1 ) V( i ) = t + a

end do

一時変数を使っても同じ

並列化してはいけない例(3)重なりのある間接参照のあるループ

46

●間接参照のあるループ

!$omp parallel dodo i = 1, 4

V( List( i ) ) = X(i) + Y(i)end do

スレッド0

・例えば、List(2) = List(4) の場合、どちらの間接参照が後にアクセスされるかによって結果が変化します。(順番に依存)

⇒ 配列List( )の値がすべて異なる(ユニークな)場合を除き並列化してはいけません。

V

スレッド1

同じ要素を更新する可能性

V( List(1) ) = X(1) + Y(1)

V( List(2) ) = X(2) + Y(2)

V( List(3) ) = X(3) + Y(3)

V( List(4) ) = X(4) + Y(4)

総和などの計算をおこなうreduction指示節を

説明します。

Reduction演算47

総和計算の並列化48

●総和の計算を並列化する!$omp parallel do

do i = 1, 4S = S + V(i)

end do

スレッド0 スレッド1

同時更新の可能性

・Sがshared属性ならば、スレッド0とスレッド1がそれぞれ勝手なタイミングでSの値を更新するため、結果は不定となります。

・もしSをprivate属性にすると、全体の総和を得ることはできません。

S

解決するには?

S = S + V(1)

S = S + V(2)

S = S + V(3)

S = S + V(4)

総和計算の並列化(cont.)49

●総和の計算を並列化する ⇒ reduction指示節

!$omp parallel do reduction (+: S)do i = 1, 4

S = S + V(i) end do

スレッド0 スレッド1

S0 = 0

S0 = S0 + V(1)

S0 = S0 + V(2)

S1 = 0

S1 = S1 + V(3)

S1 = S1 + V(4)

(注)Reduction演算は、計算の順序が逐次演算と異なります。

そのため、丸め誤差により結果が微妙に異なる可能性があります。

∵数値計算的には V(1) + V(2) + V(3) +V(4) ≠ { V(1) + V(2) } + { V(3) +V(4) }

Sは特殊なprivate変数として扱われる。(reduction変数)

初期値

加算順序はスレッド番号順とは限りません。S = S + S0 + S1 元の変数に加算

Reduction指示節50

Reduction演算 複数の変数を何らかの演算で一個の変数に縮約する操作

一般例: r = r op expr の繰り返し等(r: reduction変数, expr: rを参照しない式)

Reduction指示節により・・・ループ内でreduction変数のprivateなコピーを作成し、

ループ終了後、各スレッドの演算結果を元の変数に縮約する。

!$omp parallel do reduction(op : r1 [ , r2] )

op : reduction演算子(+ , * , - , .and. , .or. , .eqv. , .neqv. , max, min, iand, ior, ieor)

r1 [ , r2] : reduction変数(複数指定可)

総和(足し算)以外の演算にもreduction指示節が使えます。

Reduction指示節(cont.)51

表 Reduction変数の演算と初期値

Reduction変数は、ループ内では、privateな一時変数として扱われます。

ループ開始時に演算子の種類に応じて適切に初期化されます。

演算 初期値 演算 初期値

+ 0 .neqv. .false.

* 1 max 変数の型で表せる最小の値

- 0 min 変数の型で表せる最大の値

.and. .true. iand すべてのビットが 1

.or. .false. ior 0

.eqv. .true. ieor 0

オーバーヘッドとロードバランス、そして、

OpenMPで用意されているスケジューリングの方法について説明します。

DOループのスケジューリング52

オーバーヘッド53

並列化によって、プログラムの実行時間を短縮することができますが、

完全な並列化をおこなってもオーバーヘッドのため、1/4にはならない。

時間

逐次 4並列並列化

1/4

オーバーヘッドが無視できる程度の大きい処理を並列化すべき。(多重ループならば外側が望ましい。)

並列化にはオーバーヘッドがつきものです。• スレッド生成・同期• 並列化に伴うコード変更による処理の増加等

オ ー バ ー ヘ ッ ド

オ ー バ ー ヘ ッ ド

オーバーヘッド(cont.)54

!$omp parallel dodo i = 1, 10

〜〜〜end do

!$omp end parallel do

!$omp parallel dodo i = 1, 1000000

〜〜〜end do

!$omp end parallel do

反復数の少ないループより多いループの方がオーバーヘッドが相対的に小さくなります。

!$omp parallel dodo j = 1, n

do i = 1, n〜〜〜

end doend do

!$omp end parallel do

do j = 1, n!$omp parallel do

do i = 1, n〜〜〜

end do!$omp end parallel do

end do

内側ループより外側ループを並列化した方が「!$omp parallel do」の呼び出し回数が少なくオーバーヘッドが少なくなります。

ロードバランス55

均等な処理の割り振り(load balancing)にも注意する必要があります。

時間

逐次 4並列並列化

1/4

idle idle idle

不均等(インバランス)な割振りでは、期待した性能が出ないことがあります。

ループ構文とスケジューリング56

Schedule指示節により、ループ反復の割当方法を変更できます。

スケジューリング指示節 割当方法

schedule(static) 均等に分割<デフォルト>

schedule(static, chunk) chunkで指定した反復数のチャンクに分割し、スレッド番号順に巡回的に割り当てます。

schedule(dynamic [ ,chunk ] ) chunkで指定した反復数のチャンクに分割し、スレッドからの要求に応じて動的に割り当てます。各スレッドは1チャンクを実行し、次のチャンクを要求します。<chunk省略時はchunk=1>

schedule(guided [ ,chunk ] ) dynamicと同様ですが、チャンクの大きさが残りの反復数に応じて徐々に小さくなります。チャンク分割サイズはchunkで指定した値が最小になります。<chunk省略時はchunk=1>

schedule(auto) スケジューリングは、コンパイラ、および/または、実行時システムに委ねられます。

schedule(runtime) スケジューリングは、実行時の環境変数OMP_SCHEDULEによって決定されます。例: export OMP_SCHEDULE=“guided, 1”

!$omp parallel do schedule(スケジューリングの種別)

サイズ2のチャンクに分割し、順繰りに配分

STATIC(静的) スケジューリング57

Schedule指示節により、ループ反復の割当方法を変更できます。

schedule(static)

schedule(static, 2)

逐次

静的割り当て: 実行前に割り当てを決める

全てをマスタースレッドが処理

均等に分割<デフォルト>

サイズ1のチャンクに分割し、順繰りに配分schedule(static, 1)

デフォルト

!$omp parallel do schedule(スケジューリングの種別)

(■はループの1反復(1サイクル)を示す。)

schedule(dynamic, 2)

動的割り当て: 処理の終わったスレッドが次のチャンクに取りかかる。

サイズ2のチャンクに分割し、動的に割り当て (デフォルトのチャンクサイズは1)

DYNAMIC(動的) スケジューリング58

Schedule指示節により、ループ反復の割当方法を変更できます。

!$omp parallel do schedule(スケジューリングの種別)

サイクルごとの処理量が不均一な時に効果的だがstaticスケジューリングよりオーバーヘッドが大きい

thread0 thread1 thread2 thread0thread3 thread0thread2 thread3thread3 thread1thread1 thread2

スケジューリングとロードバランス59

●三角行列とロードインバランス

do j = 1, ndo i = j, n

A(i, j) = A(i, j) + B(i, j) end doend do

j

i

!$omp parallel dodo j = 1, ndo i = j, n

A(i, j) = A(i, j) + B(i, j) end doend do

単純に4並列実行

スレッド 0 1 2 3 が担当

※処理量に差 ⇒ ロードインバランス状態

スケジューリングとロードバランス(cont.)60

●ロードバランスの改善

!$omp parallel do schedule(dynamic)do j = 1, ndo i = j, n

A(i, j) = A(i, j) + B(i, j) end doend do

スレッド 0 1 2 3 0 1 2 3 0 1 2 3が担当

※ チャンクを細かくするとデフォルトのstaticよりロードバランスが改善し、負荷が均等になります。Dynamicは完璧なように思えますが、オーバーヘッドが大きいので注意が必要です。

!$omp parallel do schedule(static, 1)do j = 1, ndo i = j, n

A(i, j) = A(i, j) + B(i, j) end doend do

スレッド 0 1 2 3 3 2 1 0 1 0 3 2が担当

並列処理領域内の逐次処理

同期処理

排他制御

並列の制御・同期処理61

排他制御 同期処理

暗黙の同期62

●ループ構文などのワークシェアリング構文の出口では、暗黙に同期処理が行われます。

!$omp parallel!$omp do

do i = 1, nV(i) = V(i) + X(i)

end do!$omp end do!$omp do

do i = 1, nW(i) = W(i) + Y(i)

end do!$omp end do!$omp end parallel

※各DOループの終了時に、全スレッドの処理終了を待ってから、次のDOループの処理に移ります。待ち合わせのための若干のオーバーヘッドがかかります。

1 2 3マスタースレッド0

暗黙の同期

暗黙の同期 待ち合わせ

待ち合わせ

暗黙の同期の回避(nowait指示節)63

●暗黙の同期処理が不要ならば、nowait指示節により、同期を回避できます。

!$omp parallel!$omp do

do i = 1, nV(i) = V(i) + X(i)

end do!$omp end do nowait!$omp do

do i = 1, nW(i) = W(i) + Y(i)

end do!$omp end do!$omp end parallel

1 2 3マスタースレッド0

暗黙の同期

同期を回避

※ nowait指示節を指定すると、doループ終了時の待ち合わせをせず、直ちに次の処理に移ります。これにより待ち合わせのオーバーヘッドを減らすことができますが、誤った箇所にnowaitを指定すると不正な結果が得られることがあります。

待ち合わせ

ループ内の暗黙の同期64

●二重ループの内側を並列化する場合の

暗黙の同期

!$omp paralleldo j = 2, m

!$omp dodo i = 1, n

W(i, j) = W(i, j-1) + Y(i, j)end do

!$omp end doend do

!$omp end parallel

j = 2

j = 3

j = 4

1 2 3マスタースレッド0

※ j ループが反復するごとに同期が発生します。

待ち合わせ

待ち合わせ

ループ内の暗黙の同期の回避65

●二重ループの内側を並列化する場合の

暗黙の同期の回避

!$omp paralleldo j = 2, m

!$omp dodo i = 1, n

W(i, j) = X(i, j-1) + Y(i, j)end do

!$omp end do nowaitend do

!$omp end parallel

待ち合わせせず、次の j へ進む。

j = 2

j = 3

j = 4

1 2 3マスタースレッド0

※ j ループの前の反復の計算を全スレッドが完了するのを待つ必要が無い場合のみ、nowaitを使用できます。ループの繰り返し依存がないか注意が必要です。

j ループの反復数分の同期オーバーヘッドの節約になります。

並列実行領域中の逐次処理66

並列実行領域の中に並列化できない逐次処理を含めたい場合、並列実行領域を終わらせることなく、1つのスレッドによる逐次処理を行う領域を設けることができます。

構文 内容

single 指定された領域の処理を、一つのスレッドが実行します。 (マスタースレッドであるとは限りません)※指定領域の出口で、暗黙の同期を行います。

master 指定された領域の処理を、マスタースレッドが実行します。※指定領域の出口で、暗黙の同期は行いません。

……!$omp parallel

……!$omp single

write(6, * ) ‘Serial processing’!$omp end single

……!$omp end parallel

シングル処理一回だけ実行

並列実行領域

バリア同期(barrier構文)67

!$omp parallel……

!$omp masterallocate( V(n) )

!$omp end master!$omp barrier!$omp do

do i = 1, nV(i) = Y(i)

end do!$omp end do!$omp end parallel

※master指示節は、暗黙の同期(待ち合わせ)を行いませんので、スレッド1〜3の後の処理(緑)を待たせるためには、barrier指示節が必要です。

待ち合わせ

●barrier構文

- スレッドの待ち合わせ(同期)を行います。

暗黙の同期

1 2 3マスタースレッド0

配列Vの準備

待ち合わせ

待ち合わせ

critical構文(排他制御)68

real :: sum, xcount, functionreal :: 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 ) thenxcount = function ( xcount, x( i, j ) )

end if!$omp end critical

end do

!$omp do reduction ( +: sum )do i = 1, 4000

sum = sum + x( i, j ) + xcountend do

!$omp end parallelend do

end

スレッド0

1 2 3

一人ずつアクセスする。

critical構文は、指定範囲について複数スレッドの処理が重ならないようにし(排他制御)、アクセス競合を回避する。

その他の同期・排他処理構文69

構文 内容

atomic critical指示節と同様に排他処理をしますが、こちらの方が高速

です。ただし、インクリメントなどの特定の演算の文にのみ使用できます。

flush フラッシュ操作を行います。あるスレッドが持つ一時的なビュー(レジスタやキャッシュ等の内容)をメモリの内容と一致させます。

ordered 指定したループ領域において、逐次実行した場合と同じ順序で実行するよう順序付けを行います。

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

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

integer :: thrdnum, methrdnum = 1me = 0

!$ thrdnum = omp_get_max_threads()!$ me = omp_get_thread_num()

!$ で始まる行は、OpenMPでコンパイルする時のみ有効となります。

OpenMPを使わない場合のエラーを防ぐことができます。

integer :: thrdnum, methrdnum = omp_get_max_threads()me = omp_get_thread_num()

(プリプロセッサーの構文”#ifdef _OPENMP”を用いる方法もあります。)

以下のプログラムのomp_get_max_threads等はOpenMPのライブラリ関数なので、OpenMP並列を指定しない場合、リンク時にエラーになります。

以下のように”!$”を用いて書き換えると、通常の(OpenMPを使用しない)コンパイルの場合、コメント行と見なされ、互換性を保つことができます。

環境変数74

環境変数 内容

OMP_NUM_THREADS 並列実行領域で使用するスレッドの数を設定します。

OMP_SCHEDULE スケジュールタイプがruntimeであるループ指示文のスケジューリングを制御します。(デフォルトはstatic)

OMP_STACKSIZE 各スレッドが実行時に利用するスタックサイズを指定します。スレッドごとの固有データなどのメモリ領域に利用されます。

(注)OMP_STACKSIZE大きなprivate属性の配列を用いるプログラムでは、スレッドのprivate用のスタックサイズが不足する場合があります。そのような場合は、この環境変数を十分大きい値で設定します。

OpenMPプログラムの実行に影響する主な環境変数

OpenMP並列化例

OpenMPに限らず並列化の一般知識の補足

OpenMPに関する情報源

補足75

OpenMPによる並列化の簡単な例

OpenMP並列化例76

※ ここでは、OpenMP並列化の説明を行います。逐次での高速化は「チューニング技法」の資料を参考にしてください。

OpenMP並列化例:個別要素法/分子動力学法の例

77

各粒子にかかる力の計算 入力データ

粒子数 n 、粒子の座標 x(n) 簡単のため1次元とする。

粒子i,j間の相互作用 fij 簡単のため、距離に反比例

fij = 1/(x(j) - x(i))

各粒子にかかる力の計算 粒子1にかかる力f(1)の場合

f(1)= f12 +f13 +f14 +f15 +f16

これをf(1)〜f(6)の全ての粒子について計算 図 粒子1にかかる力(赤矢印)

1

2

3

5

6

4

1f12 f16

f13f15

f14

n = 6

粒子iにかかる力の合計をf(i)に保存

f(1) = f12 +f13 +f14 +f15 +f16

f(2) = -f12 +f23 +f24 +f25 +f26

f(3) = -f13 -f23 +f34 +f35 +f36

f(4) = -f14 -f24 -f34 +f45 +f46

f(5) = -f15 -f25 -f35 -f45 +f56

f(6) = -f16 -f26 -f36 -f46 -f56

作用反作用の法則 (fji = –fij)より、

対称な要素は逆符号で同じ値。

OpenMP並列化例:個別要素法/分子動力学法の例

78

図 作用反作用の法則 (fji = –fij)

1

2

3

5

6

4

1f12 f16

f13f15

f14

-f15

-f16

-f13

-f14

f21= -f12n = 6

OpenMP並列化例:逐次プログラム

79

dimension x(n), f(n)do i=1,n-1do j=i+1,nfij = 1.d0/(x(j)-x(i))

f(i) = f(i) + fijf(j) = f(j) - fij

end doend do

n: 粒子数x(n): 粒子のx座標の配列f(n): 粒子にかかる力の配列

一つfijを計算したら、作用・反作用がかかる配列fの2粒子の要素に加算。

粒子i,jのループ(i<j)

f(1)= f12 +f13 +f14 +f15 +f16

f(2)= -f12 +f23 +f24 +f25 +f26

f(3)= -f13 -f23 +f34 +f35 +f36

f(4)= -f14 -f24 -f34 +f45 +f46

f(5)= -f15 -f25 -f35 -f45 +f56

f(6)= -f16 -f26 -f36 -f46 -f56

i=1 i=2 i=3 i=4 i=5

図 粒子数 n = 6 の場合の計算内容

f(1)= f12 +f13 +f14 +f15 +f16

f(2)= -f12

f(3)= -f13

f(4)= -f14

f(5)= -f15

f(6)= -f16

i=1

i=1の演算実行後の配列 f の状態

最終結果fij: 粒子iへのjによる力

の計算

n = 6

OpenMP並列化例:OpenMP並列化時の検討項目

80

並列化時の検討項目(1)並列化するループの選択

(2)並列化可能性(計算の順番の依存性)の検討

(3)変数のデータ共有属性の設定

(4)スケジューリングの選択

do i=1,n-1do j=i+1,nfij = 1.d0/(x(j)-x(i))f(i) = f(i) + fijf(j) = f(j) - fij

end doend do

OpenMP並列化例:(1)並列化するループの選択

81

オーバーヘッドの観点から、

外側のループの並列化が望ましい。

do i=1,n-1do j=i+1,nfij = 1.d0/(x(j)-x(i))f(i) = f(i) + fijf(j) = f(j) - fij

end doend do 結論:外側の i に関する

ループを並列化したい。

!$OMP PARALLEL DO ...

OpenMP並列化例:(2)並列化可能性の検討

82

並列化可能性(計算の順番の依存性)の検討

i の加算・減算の順番を変えても配列 f は不変

→ 依存性無し (丸め誤差は存在する。)

f(1)= f12 +f13 +f14 +f15 +f16

f(2)= -f12 +f23 +f24 +f25 +f26

f(3)= -f13 -f23 +f34 +f35 +f36

f(4)= -f14 -f24 -f34 +f45 +f46

f(5)= -f15 -f25 -f35 -f45 +f56

f(6)= -f16 -f26 -f36 -f46 -f56

i=1 i=2 i=3 i=4 i=5

どのiを先に計算しても最終的な配列f(i)の値は同じ。

結論: 並列化可能

OpenMP並列化例:(3)変数のデータ共有属性の設定

83

スレッド間のアクセス競合が発生する変数を洗い出す。

f(1)= f12 +f13 +f14 +f15 +f16

f(2)= -f12 +f23 +f24 +f25 +f26

f(3)= -f13 -f23 +f34 +f35 +f36

f(4)= -f14 -f24 -f34 f(4)= +f45 +f46

f(5)= -f15 -f25 -f35 f(5)= -f45 +f56

f(6)= -f16 -f26 -f36 f(6)= -f46 -f56

i=1 i=2 i=3 i=4 i=5

スレッド0の計算(i=1〜3)

上の例では、スレッド間で要素f(4)〜f(6)の同時更新(競合)のおそれがあるので、スレッドはそれぞれ独自の f を持つ必要がある。また、最後に各スレッドの f を要素ごとに合計する必要があるので、配列fをreductionで指定する必要がある。

スレッド1の計算(i=4〜5)

配列fに関する演算の2スレッド実行の例

OpenMP並列化例:(3)変数のデータ共有属性の設定 cont.

84

スレッド間のアクセス競合が発生する変数を洗い出す。

特に並列ループ内の左辺にある変数に注意する。

!$OMP PARALLEL DO ...do i=1,n-1do j=i+1,nfij = 1.d0/(x(j)-x(i))f(i) = f(i) + fijf(j) = f(j) - fij

end doend do

fijの値はスレッドごとに異なる→要private化

ループ変数 j は自動的にprivate。

!$OMP PARALLEL DO REDUCTION(+:f) PRIVATE(fij)

結論:配列fのreduction変数化変数fijのprivate化が必要

!$OMP PARALLEL DO REDUCTION(+:f)!$OMP& PRIVATE(fij) SCHEDULE(STATIC,1)

do i=1,n-1....

OpenMP並列化例:(4)スケジューリングの選択

85

f12 +f13 +f14 +f15 +f16

-f12 +f23 +f24 +f25 +f26

-f13 -f23 +f34 +f35 +f36

-f14 -f24 -f34 +f45 +f46

-f15 -f25 -f35 -f45 +f56

-f16 -f26 -f36 -f46 -f56

f12 +f13 +f14 +f15 +f16

-f12 +f23 +f24 +f25 +f26

-f13 -f23 +f34 +f35 +f36

-f14 -f24 -f34 +f45 +f46

-f15 -f25 -f35 -f45 +f56

-f16 -f26 -f36 -f46 -f56

!$OMP PARALLEL DO REDUCTION(+:f)!$OMP& PRIVATE(fij)

do i=1,n-1....

●デフォルトのスケジューリング ●チャンクサイズを小さく(1に)した例

インバランス大 インバランスが緩和

24: 6 18:12

加速率=30/24 = 1.25 加速率=30/18= 1.67

6粒子・2スレッドでのロードバランス 6粒子・2スレッドでのロードバランス

スレッド0 スレッド1 スレッド0 スレッド1

OpenMP並列化例(まとめ)86

個別要素法/分子動力学法の例

並列化時の検討項目(1)並列化するループの選択

外側が望ましい。

(2)並列化可能性

(計算の順番の依存性)の検討並列ループの各サイクルの実行順が、

変わっても同じ結果か確認する。

(3)変数のデータ共有属性の設定左辺の変数に注意し、必要に応じて、

PRIVATEやREDUCTIONの宣言をする。

(4)スケジューリングの選択インバランスを緩和するような

スケジューリングを選択する。

※複数CPU/ノードの機種(cc-NUMA)の場合、

ファーストタッチの検討も必要(本講座では省略)

!$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) + fijf(j) = f(j) - fij

! ここで j ≠ i の要素f(j)を更新する。! 総和の計算 → 要reduction変数化

end doend do

並列化率とアムダールの法則87

アムダールの法則オーバーヘッドを無視した理想的な条件でも

速度向上率の上限は逐次部の割合(1-p)で決まってしまいます。

時間

逐次

逐次部

∞並列

1-p

p

逐次部

1/(1-p)倍が速度向上率の上限。

並列化率80% ( p = 0.8 ) ならば、1/0.2 = 5倍が上限

予定の並列数にふさわしい並列化率以上である必要があります。

並列化済

並列化率とアムダールの法則(cont.)88

アム

ダー

ル則

によ

る速

度向

上率

並列数(N)

1-p = 1/2

1-p = 1/4

1-p = 0逐次部の割合(1-p)が1/(並列数)以下になるよう並列化を進めましょう。

1-p = 1/8

速度向上率

(p:並列化率,N:並列数)

アムダールの法則

(並列化率100%)

(88%)

(75%)

(50%)

2.3 2.9

逐次部の割合

4.3

1.8

情報源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

Sections構文

特殊なデータ属性

おまけ90

Sections構文91

※処理量のバラツキが大きいと並列化の効果が出にくくなります。

処理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

Sections構文による並列化

Single Program, Multiple Data streams (SPMD) 処理A,B,C,Dの中のループが並列化不能でも、処理A,B,C,D

の間に依存性がなく独立に実行できるならば、並列に実行できます。

FirstPrivate/LastPrivate

共有変数がある場合の注意(Threadprivate指示文)

特殊なデータ属性92

Firstprivate属性93

プログラム開始

マスタースレッド0

firstprivate( t )

t = 2.0

!$omp parallel do firstprivate(t)do i = 1, 4000

if (i.gt.nmax) t = 0.0V(i) = X(i) + t * Y(i)

end do

●Firstprivate指示節

- Private変数と同様の属性を持ちますが、並列実行領域に入る直前の値で、各スレッドの値が初期化されます

マスタースレッド0

t0= 2.0スレッド1

t1= 2.0スレッド2

t2= 2.0スレッド3

t3= 2.0

t= 2.0 V X Y shared(V, X, Y)

Lastprivate属性94

プログラム開始

マスタースレッド0

lastprivate( i )

!$omp parallel do lastprivate(i)do i = 1, 4000

V(i) = X(i) + Y(i)end doV(i) = X(i) !!! i = 4001

●Lastprivate指示節

-Private変数と同様の属性をもちますが、並列実行領域後に、ループの逐次的な終値に相当する反復後の値が代入されます。

V X Y shared(V, X, Y)

マスタースレッド0

i0i=1,1000

スレッド1

i1i=1001,2000

スレッド2

i2i=2001,3000

スレッド3

i3i=3001,4000

マスタースレッド0

i=4001i=4000の反復(逐次実行した場合の最終に相当する反復)終了後の値

共有変数がある場合の注意95

COMMONブロック/SAVE変数

Threadprivate指示文の説明

Threadprivate指示文96

以下のとき、threadprivate指示文を使います。

複数のルーチンからアクセスする変数(commonブロック変数・SAVE変数・module変数)がある。

かつ、その変数が、スレッドごとに異なる値を持つ必要がある。(=shared属性では×)

shared属性

並列実行領域内のすべてのス

レッドからアクセス可能な

共有データ

スレッド0 threadprivate属性ルーチンA,B の共通データ

ルーチンA固有なデータ ルーチンB

固有なデータ

スレッド1 threadprivate属性ルーチンA,B の共通データ

ルーチン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変数の初期化

マスタースレッド以外のスレッドのthreadprivate変数は、自動的に初期化されません。Copyin指示節により、並列実行領域の開始時にマスタースレッドのデータの内容を各スレッドにコピーします。

Copyprivate指示節 Single領域(並列領域内の逐次領域)終了後の各スレッドへのデータ転送

Single構文の終りにSingle実行スレッドの変数を、他のスレッドの対応する変数へデータをブロードキャスト(コピー)します。

!$omp parallel copyin ( /com/ )

!$omp end single copyprivate ( /com/ )

ThreadPrivate指示文の使用例99

program routine_Ainclude '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

subroutine routine_Bcommon /com/id

!$omp threadprivate ( /com/ )write(*,*) ’id=‘, idend

Threadprivate変数 id は、routine_A, routine_Bの両方からアクセス可。

変数 id の値はスレッドごとに異なる。

2つ目の並列実行領域でも各スレッドごと

の値が保存される。

並列実行領域1

並列実行領域2

2つのルーチンが参照するcommonブロックをthreadprivate化する例を示します。

メインルーチン: 「routine_A」 サブルーチン: 「routine_B」

おわり100

ご清聴ありがとうございました。

2018年10月

一般財団法人高度情報科学技術研究機構 (著作者 )

本資料を教育目的等で利用いただいて構いません。利用に際しては以下の点に留意いただくとともに、下記のヘルプデスクにお問い合わせ下さい。 本資料は、構成・文章・画像などの全てにおいて著作権法上の保護を受

けています。 本資料の一部あるいは全部について、いかなる方法においても無断での

転載・複製を禁じます。 本資料に記載された内容などは、予告なく変更される場合があります。 本資料に起因して使用者に直接または間接的損害が生じても、著作者は

いかなる責任も負わないものとします。

問い合わせ先:ヘルプデスク helpdesk[-at-]hpci-office.jp([-at-]を@にしてください)

top related