45

OpenMP 標準では、C++ 98 (ISO/IEC 14882:1998) のみが考慮されます。これは、C++03 または C++11 でさえ、OpenMP の使用法をサポートする標準がないことを意味します。したがって、C++ >98 および OpenMP を使用するプログラムは標準外で動作します。つまり、特定の条件下で動作する場合でも、移植可能である可能性は低く、保証されることはありません。

独自のマルチスレッド サポートを備えた C++11 の場合、状況はさらに悪化し、特定の実装では OpenMP と衝突する可能性が非常に高くなります。

では、C++03 および C++11 で OpenMP を使用することはどの程度安全なのでしょうか?

C++11 マルチスレッドと OpenMP を同じプログラムで安全に使用できますが、それらをインターリーブする必要はありません (つまり、C++11 並行機能に渡されるコードに OpenMP ステートメントがなく、スレッドに C++11 並行性がない) OpenMP によって生成された)?

私が特に興味を持っているのは、最初に OpenMP を使用していくつかのコードを呼び出し、次に同じデータ構造で C++11 の同時実行性を使用して別のコードを呼び出すという状況です。

4

5 に答える 5

28

Walter さん、私は他のディスカッションで現在の状況を説明しただけでなく、情報源 (つまり、OpenMP 言語委員会の一員である私の同僚) から直接情報を提供したと思います。

OpenMP は、FORTRAN および C への軽量のデータ並列追加として設計され、後に C++ イディオム (ランダムアクセス反復子に対する並列ループなど) および明示的なタスクの導入によるタスク並列処理に拡張されました。可能な限り多くのプラットフォーム間で移植可能であり、3 つの言語すべてで本質的に同じ機能を提供することを目的としていますその実行モデルは非常に単純です。シングルスレッド アプリケーションはスレッドのチームを並列領域でフォークし、内部でいくつかの計算タスクを実行してから、チームを結合してシリアル実行に戻します。ネストされた並列処理が有効になっている場合、並列チームの各スレッドは後で独自のチームをフォークできます。

OpenMP の主な用途はハイ パフォーマンス コンピューティングであるため (結局、そのディレクティブと実行モデルはハイ パフォーマンス Fortran から借用されました)、OpenMP 実装の主な目標は効率であり、他のスレッド パラダイムとの相互運用性ではありません。一部のプラットフォームでは、OpenMP ランタイムがプロセス スレッドを制御する唯一のランタイムである場合にのみ、効率的な実装を実現できます。また、OpenMP には、他のスレッド構造ではうまく機能しない可能性がある特定の側面もあります。たとえば、OMP_THREAD_LIMIT2 つ以上の並列並列領域をフォークするときに設定されるスレッド数の制限などです。

OpenMP 標準自体は、他のスレッド パラダイムの使用を厳密に禁止していませんが、相互運用性を標準化していないため、そのような機能のサポートは実装者次第です。つまり、最上位の OpenMP 領域の安全な同時実行を提供する実装もあれば、提供しない実装もあるということです。x86 の実装者がサポートを約束しているのは、そのほとんどが他の実行モデル (Cilk と TBB を使用する Intel、C++11 を使用する GCC など) の支持者でもあり、x86 は通常「実験的な」プラットフォームと見なされているためです (他のベンダーは通常、はるかに保守的です)。

また、OpenMP 4.0 が採用する C++ 機能については、ISO/IEC 14882:1998 を超えることはありません (SC12 ドラフトはこちら)。標準には現在、ポータブル スレッド アフィニティのようなものが含まれています。これは、OpenMP のバインディング メカニズムと衝突する独自のバインディング メカニズムを提供する可能性のある他のスレッド パラダイムとは確実にうまく機能しません。ここでも、OpenMP 言語は HPC (データとタスクの並列科学および工学アプリケーション) を対象としています。C++11 コンストラクトは、汎用コンピューティング アプリケーションを対象としています。派手な C++11 の並行機能が必要な場合は、C++11 のみを使用するか、OpenMP と組み合わせる必要が本当にある場合は、移植性を維持したい場合は、言語機能の C++98 サブセットに固執します。

私が特に興味を持っているのは、最初に OpenMP を使用していくつかのコードを呼び出し、次に同じデータ構造で C++11 の同時実行性を使用して別のコードを呼び出すという状況です。

可能にしたくないことについて明確な理由はありませんが、それは OpenMP コンパイラとランタイム次第です。並列実行に OpenMP を使用する無料の商用ライブラリ (MKL など) がありますが、(ユーザー マニュアルの奥深くに隠されている場合もありますが) 何がいつ可能なのかについての情報を提供するマルチスレッド コードとの非互換性に関する警告が常に表示されます。いつものように、これは OpenMP 標準の範囲外であり、したがって YMMV の範囲外です。

于 2012-12-12T12:24:11.750 に答える
7

私は実際に高性能コンピューティングに興味がありますが、OpenMP (現在) は私の目的を十分に果たしていません: 柔軟性が十分ではありません (私のアルゴリズムはループベースではありません)

多分あなたは本当にTBBを探していますか?これは、ループおよびタスク ベースの並列処理、および標準 C++ でのさまざまな並列データ構造のサポートを提供し、移植性とオープン ソースの両方を備えています。

(完全な免責事項: 私は TBB に深く関わっている Intel で働いていますが、実際には TBB ではなく OpenMP に取り組んいます :-); 私は確かにインテルのために話しているのではありません!)。

于 2012-12-13T09:27:05.840 に答える
5

ジム・カウニーのように、私もインテルの従業員です。Intel Threading Building Blocks(Intel TBB)は、OpenMPのようなループレベルの並列処理だけでなく、他の並列アルゴリズム、並行コンテナー、および低レベルの機能も備えているため、適切なオプションである可能性があることに同意します。そして、TBBは現在のC++標準に追いつくように努めます。

また、Walterを明確にするために、Intel TBBには、parallel_reduceアルゴリズムと、アトミックおよびミューテックスの高レベルのサポートが含まれています。

インテル®スレッディングビルディングブロックのユーザーガイドは、http://software.intel.com/sites/products/documentation/doclib/tbb_sa/help/tbb_userguide/title.htm にあります。ユーザーガイドには、図書館。

于 2012-12-17T18:06:10.497 に答える
4

OpenMP は多くの場合 (私は例外を認識していません)、Pthreads の上に実装されるため、C++11 の同時実行性が Pthread コードとどのように相互運用するかを考えることで、相互運用性に関するいくつかの質問について考えることができます。

複数のスレッド モデルの使用によるオーバーサブスクリプションが問題になるかどうかはわかりませんが、これは間違いなく OpenMP の問題です。OpenMP 5 でこれに対処する提案があります。それまでは、これをどのように解決するかは実装定義です。それらは重いハンマーですが、OMP_WAIT_POLICY(OpenMP 4.5+)、KMP_BLOCKTIME(Intel および LLVM)、およびGOMP_SPINCOUNT(GCC) を使用してこれに対処できます。他の実装にも似たようなものがあると確信しています。

相互運用性が重要な問題の 1 つは、メモリ モデル、つまりアトミック操作の動作方法です。これは現在定義されていませんが、それでも推論することができます。たとえば、OpenMP 並列処理で C++11 アトミックを使用する場合は問題ありませんが、OpenMP スレッドから C++11 アトミックを正しく使用する責任があります。

OpenMP アトミックと C++11 アトミックを混在させることは、悪い考えです。私たち (OpenMP 5 ベース言語のサポートを担当する OpenMP 言語委員会ワーキング グループ) は現在、この問題の解決に取り組んでいます。個人的には、C++11 アトミックはあらゆる点で OpenMP アトミックよりも優れていると思うので、アトミックには C++11 (または C11、または__atomic) を使用#pragma omp atomicし、Fortran プログラマーに任せることをお勧めします。

以下は、OpenMP スレッドで C++11 アトミックを使用するサンプル コードです。テストしたすべての場所で設計どおりに動作します。

完全な開示: Jim と Mike のように、私は Intel で働いています :-)

#if defined(__cplusplus) && (__cplusplus >= 201103L)

#include <iostream>
#include <iomanip>

#include <atomic>

#include <chrono>

#ifdef _OPENMP
# include <omp.h>
#else
# error No OpenMP support!
#endif

#ifdef SEQUENTIAL_CONSISTENCY
auto load_model  = std::memory_order_seq_cst;
auto store_model = std::memory_order_seq_cst;
#else
auto load_model  = std::memory_order_acquire;
auto store_model = std::memory_order_release;
#endif

int main(int argc, char * argv[])
{
    int nt = omp_get_max_threads();
#if 1
    if (nt != 2) omp_set_num_threads(2);
#else
    if (nt < 2)      omp_set_num_threads(2);
    if (nt % 2 != 0) omp_set_num_threads(nt-1);
#endif

    int iterations = (argc>1) ? atoi(argv[1]) : 1000000;

    std::cout << "thread ping-pong benchmark\n";
    std::cout << "num threads  = " << omp_get_max_threads() << "\n";
    std::cout << "iterations   = " << iterations << "\n";
#ifdef SEQUENTIAL_CONSISTENCY
    std::cout << "memory model = " << "seq_cst";
#else
    std::cout << "memory model = " << "acq-rel";
#endif
    std::cout << std::endl;

    std::atomic<int> left_ready  = {-1};
    std::atomic<int> right_ready = {-1};

    int left_payload  = 0;
    int right_payload = 0;

    #pragma omp parallel
    {
        int me      = omp_get_thread_num();
        /// 0=left 1=right
        bool parity = (me % 2 == 0);

        int junk = 0;

        /// START TIME
        #pragma omp barrier
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {

            if (parity) {

                /// send to left
                left_payload = i;
                left_ready.store(i, store_model);

                /// recv from right
                while (i != right_ready.load(load_model));
                //std::cout << i << ": left received " << right_payload << std::endl;
                junk += right_payload;

            } else {

                /// recv from left
                while (i != left_ready.load(load_model));
                //std::cout << i << ": right received " << left_payload << std::endl;
                junk += left_payload;

                ///send to right
                right_payload = i;
                right_ready.store(i, store_model);

            }

        }

        /// STOP TIME
        #pragma omp barrier
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        #pragma omp critical
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << junk << std::endl;
        }
    }

    return 0;
}

#else  // C++11
#error You need C++11 for this test!
#endif // C++11
于 2016-10-04T19:53:53.690 に答える
0

OpenMP 5.0 では、C++11 に対する相互作用が定義されるようになりました。ただし、一般的にC++ 11以降のものを使用すると、「未指定の動作が発生する可能性があります」 .

この OpenMP API 仕様では、ISO/IEC 14882:2011 を C++11 と呼んでいます。OpenMP 仕様の将来のバージョンでは、次の機能に対応する予定ですが、現在、それらを使用すると、未指定の動作が発生する可能性があります。

  • アライメントサポート
  • 標準レイアウトタイプ
  • ムーブ コンストラクトがスローできるようにする
  • move 特殊メンバー関数の定義
  • 同時実行
  • データ依存性の順序付け: アトミックとメモリ モデル
  • 標準ライブラリへの追加
  • スレッドローカル ストレージ
  • 同時実行による動的な初期化と破棄
  • C++11 ライブラリ
于 2018-11-28T10:00:41.597 に答える