「デジタル」がどのように収束しなかったかの物語









少し前に、再現可能な結果を​​得る方法と、これに関連する困難について書きました。 また、コンパイラで浮動小数点数を使用して作業を制御できるモデルについて詳しく説明し、ライブラリまたは標準を使用する場合は、それらに必要なフラグが指定されていることに注意する必要があることを個別に明確にしました。 そしてつい最近、OpenMPを使用した場合の結果の再現性に正確に関連する興味深い問題に出会いました。



再現性とは何ですか? はい、すべてがシンプルです-私たちにとって重要なので、発売から発売まで同じ「良いデジタル」を手に入れたいです。 これは、並列計算が現在積極的に使用されている多くの分野で重要です。



したがって、覚えているように、合計の順序は機械計算で重要な役割を果たします。また、任意のテクノロジを使用してサイクルを並列化すると、合計の実行順序が誰にもわからないため、結果の再現性の問題が避けられません元のサイクルでいくつの「ピース」が破損するか。 これは、OpenMPを縮小で使用する場合に特に当てはまります。



簡単な例を考えてみましょう。



!$OMP PARALLEL DO schedule(static) do i=1,n a=a+b(i) enddo
      
      





この場合、静的スケジューラーを使用すると、単純に反復スペース全体を等しい数のパーツに分割し、各スレッドにこれらの反復を実行させることができます。 たとえば、1000回の反復がある場合、4つのスレッドが動作すると、「兄弟ごとに」250の反復が得られます。 この配列は、異なるスレッドの共通データの例です。そのため、コードのセキュリティに注意する必要があります。 リダクションを使用して各スレッドでその値を計算し、取得した「中間」結果を追加することは完全に機能するオプションです。



 !$OMP PARALLEL DO REDUCTION(+:a) schedule(static) do i=1,n a=a+b(i) enddo
      
      





したがって、このような単純な例でも、値の広がりを取得するのは非常に簡単です。

OMP_SET_NUM_THREADSを使用してスレッド数を変更すると、2スレッドa = 204.5992で、4スレッドですでに204.6005になりました。 配列b(i)aを初期化する方法は省略しました。



興味深いのは、多くの条件が満たされている場合にのみ、再現可能な結果について話すことができることです。 したがって、アーキテクチャ、OS、アプリケーションが組み込まれたコンパイラのバージョン、およびスレッドの数は、開始から開始まで常に一定でなければなりません。 スレッドの数を変更すると、結果は異なり、これはまったく正常です。 それでも、これらの条件がすべて満たされたとしても、結果は異なる場合があります。ここでは、環境変数KMP_DETERMINISTIC_REDUCTIONと静的スケジューラーが役立ちます。 アプリケーションのパラレルバージョンとシリアルバージョンの結果、および多数のスレッドを使用した別の起動の結果の一致を保証するものではありません。 これは理解することが重要です。



これはかなり狭いケースであり、実際に何も変更しなかったため、結果は一致しませんでした。 そして、主な驚きは、 KMP_DETERMINISTIC_REDUCTIONが機能しない場合もありますが、「ルールに従って」いることです。

このようなコードは、最初の例よりも少し複雑ですが、異なる結果が得られます。



 !$OMP PARALLEL DO REDUCTION(+:ue) schedule(static) do is=1,ns do y=1,ny do x=1,nx ue(x,y)=ue(x,y) + ua(x,y,is) enddo enddo enddo !$OMP END PARALLEL DO
      
      





KMP_DETERMINISTIC_REDUCTION変数が設定された後でも、何も変更されていません。 なんで? コンパイラーは、パフォーマンス上の理由から、ロックを使用して独自のループ実装を作成し、結果を気にしない場合があります。 これらのケースは、アセンブラーで簡単に追跡できます。 「良い」ケースでは、 __ kmp_reduce_nowait関数を呼び出す必要があります。 しかし、私の例では、これは行われていないため、 KMP_DETERMINISTIC_REDUCTIONの信頼性がいくらか損なわれます。



そのため、コードを記述し、計算の結果が実行から実行にジャンプする場合は、頭に灰を振りかけないでください。 最適化をオフにして、アプリケーションを実行します。 データが整列しているかどうかを確認し、浮動小数点数を扱うための「厳密な」モデルを設定します。 次のコンパイラオプションは、テストに役立つ場合があります。



 ifort -O0 -openmp -fp-model strict -align array32byte
      
      





このオプションセットで結果が驚く場合は、OpenMPとリダクションで並列化されたループを確認し、 KMP_DETERMINISTIC_REDUCTIONを有効にしてください 。 これで問題が解決する場合があります。 そうでない場合は、アセンブラーを見て、 __ kmp_reduce_nowait呼び出しを確認してください 。 この呼び出しが利用可能な場合、問題はおそらくOpenMPまたはコンパイラにありませんが、エラーがコードに忍び込んでいます。 ところで、 KMP_DETERMINISTIC_REDUCTIONの問題をすぐに解決する必要があります。 しかし、今この機能を検討してください。



All Articles