付録 B: その他のスレッドパッケージとの併用

付録 B: その他のスレッドパッケージとの併用#

正しい相互運用性#

oneTBB は他のスレッドパッケージでも使用できます。追加の作業は必要ありません。

以下は、外側のループを OpenMP* で並列化し、内側のループを oneTBB で並列化する例です。

int M, N; 

struct InnerBody { 
    int i; 
    void operator()(tbb::blocked_range<int> const& r) const { 
        for (auto j = r.begin(); j != r.end(); ++j) { 
            // (i, j) 要素のワークを実行 
        } 
    } 
}; 

void TBB_NestedInOpenMP() { 
#pragma omp parallel 
    { 
#pragma omp for 
        for(int i = 0; i < M; ++i) { 
            tbb::parallel_for(tbb::blocked_range<int>(0, N, 10), InnerBody(i)); 
        } 
    } 
}

#pragma omp parallel は、OpenMP* にスレッドのチームを作成するように指示します。各スレッドは、ディレクティブに関連付けられたコードブロックのステートメントを実行します。

#pragma omp for は、コンパイラーが次のループの反復を既存のスレッドチーム内のスレッド間で分散し、ループ本体の並列実行を可能にすることを示します。

POSIX* スレッドの同様の例を参照してください。

int M, N; 

struct InnerBody { 
    int i; 
    void operator()(tbb::blocked_range<int> const& r) const { 
        for (auto j = r.begin(); j != r.end(); ++j) { 
            // (i, j) 要素のワークを実行 
        } 
    } 
}; 

void* OuterLoopIteration(void* args) { 
    int i = reinterpret_cast<intptr_t>(args); 
    tbb::parallel_for(tbb::blocked_range<int>(0, N, 10), InnerBody(i)); 
    return nullptr; 
} 

void TBB_NestedInPThreads() { 
    std::vector<pthread_t> id(M); 
    // 外側のループの繰り返しごとにスレッドを作成 
    for(int i = 0; i < M; ++i) { 
        std::intptr_t arg = i; 
        pthread_create(&id[i], NULL, OuterLoopIteration, (void*)arg); 
    } 
    // 外側のループスレッドが終了するまで待機 
    for(int i = 0; i < M; ++i) 
        pthread_join(id[i], NULL); 
}

過剰な CPU の使用を避ける#

実行の正当性に影響することなく、oneTBB を他のスレッドパッケージと安全に使用できますが、複数のスレッドプールから多数のスレッドを同時に実行すると、オーバーサブスクリプションが発生する可能性があります。これにより、システムリソースが過剰に使用され、実行パフォーマンスに影響することになります。

ネストされた並列処理を使用した前述の例で、並列ループ内で OpenMP* 並列領域が実行される場合を考えてみましょう。

int M, N; 

void InnerBody(int i, int j) { 
    // (i, j) 要素のワークを実行 
} 

void OpenMP_NestedInTBB() { 
    tbb::parallel_for(0, M, [&](int i) { 
        #pragma omp parallel for 
        for(int j = 0; j < N; ++j) { 
            InnerBody(i, j); 
        } 
    }); 
}

OpenMP* 並列領域のセマンティクスにより、この並列ランタイムの構成では、同時に実行されるスレッドの数が 2 乗になる可能性があります。このようなオーバーサブスクリプションによりパフォーマンスが低下する可能性があります。

oneTBB は、スレッド・コンポーザビリティー・マネージャー (TCM) を使用してこの問題を解決します。これは、異なるスレッドランタイム間の連携を向上させる実験的な CPU リソースの調整レイヤーです。

デフォルトでは、TCM は無効になっています。有効にするには、TCM_ENABLE 環境変数を 1 に設定します。意図どおりに動作することを確認するには、アプリケーションを実行する前に TCM_VERSION 環境変数を 1 に設定し、TCM: で始まる行の出力を確認します。TCM: TCM_ENABLE 1 の行は、スレッド・コンポーザビリティー・マネージャーがアクティブであることを示します。

出力例。

TCM: VERSION 1.3.0 
<...> 
TCM: TCM_ENABLE 1

インテル® DPC++/C++ コンパイラーの OpenMP* 実装と併用すると、TCM により、上記と同様のシナリオで過剰なスレッドの同時スケジュールを回避できます。

oneTBB GitHub Issues または Discussions を通じて、スレッド・コンポーザビリティー・マネージャーに関するフィードバックを送信したり、質問したりしてください。

CPU リソースの使用を調整するには、スレッド・コンポーザビリティー・マネージャーのサポートが必要です。最適な調整のため、アプリケーション内の各スレッドパッケージが TCM と統合されていることを確認してください。

関連情報