質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • C++

    3791questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C++のOpenMPの入れ子(ネスト)とスレッド番号の仕様について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 354

m_yoko

score 139

 前提・実現したいこと

C++でOpenMPを利用したプログラムを書いているのですが、parallel forを入れ子にした状態でスレッドの番号を重複せずに取得したいです。
入れ子にした状態だと、番号が重複してしまいます。
期待するスレッドの番号を重複せずに連番で得られるようにしたいです。
仕様的にomp_get_thread_num()は最後のスレッド分割した際の番号を取得するものなのでしょうか?
前段と後段の間でスレッドの番号の数を取得し、その数*後段のスレッド分割数をすれば計算することができるのはわかります。

 発生している問題・エラーメッセージ

前段と後段でそれぞれスレッド分割をしているので、期待するスレッドの番号は0~11なのですが、実際は0~5がそれぞれ重複して表示されます。
内部の動作的には実際のスレッド番号が後段のスレッド分割の部分だけのスレッド番号だけになってしまい、それが前段で2回分割されていると思われます。

 該当のソースコード

#include <omp.h>
#include <iostream>

int main()
{
    omp_set_dynamic(1);
    omp_set_nested(1);
#pragma omp parallel for 
    for (int i = 0; i < 2; i++)
    {
#pragma omp parallel for 
        for (int j = 0; j < 6; j++)
        {
#pragma omp critical 
            {
                std::cout << omp_get_thread_num() << std::endl;
            }
        }
    }
    return 0;
}

 補足情報(FW/ツールのバージョンなど)

VS2017

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

仕様的にomp_get_thread_num()は最後のスレッド分割した際の番号を取得するものなのでしょうか?

はい。規格的にはそういうのを「チーム」と呼んでますが、omp_get_thread_num は、チーム内での一意な 0〜 の連番を返します。

今回のようにネストしているケースでは、別々のチームが並行に実行されているため、この番号が重複して見えるということです。

なお、OpenMP規格的には、プログラム全体での稼働/待機スレッドを確実に識別する仕組みは無いと思います。

ところでLinuxの場合、OSで管理しているスレッドはgettidシステムコールというOSのAPIで識別できます。次のようにOSでのスレッド情報も出力するコードについて、CentOS7付属のgccでコンパイルし実行した結果を参考までに載せます。
番号が重複していても、スレッドとして別になっていることが分かります。

#include <omp.h>
#include <iostream>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <unistd.h>
#include <syscall.h>

int main()
{
    omp_set_dynamic(1);
    omp_set_nested(1);
#pragma omp parallel for 
    for (int i = 0; i < 2; i++)
    {
#pragma omp parallel for 
        for (int j = 0; j < 6; j++)
        {
#pragma omp critical 
            {
                std::cout << "i=" << i << ", j=" << j << ", tid(omp)=" << omp_get_thread_num() << ", tid(OS)=" << syscall(SYS_gettid) << std::endl;
            }
        }
    }
    return 0;
}
$ ./a.out
i=0, j=1, tid(omp)=1, tid(OS)=30522
i=0, j=2, tid(omp)=2, tid(OS)=30523
i=0, j=5, tid(omp)=5, tid(OS)=30527
i=0, j=4, tid(omp)=4, tid(OS)=30525
i=0, j=3, tid(omp)=3, tid(OS)=30524
i=0, j=0, tid(omp)=0, tid(OS)=30490
i=1, j=2, tid(omp)=2, tid(OS)=30528
i=1, j=5, tid(omp)=5, tid(OS)=30533
i=1, j=4, tid(omp)=4, tid(OS)=30532
i=1, j=3, tid(omp)=3, tid(OS)=30530
i=1, j=1, tid(omp)=1, tid(OS)=30526
i=1, j=0, tid(omp)=0, tid(OS)=30491

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/07 18:28

    ありがとうございます。チームの概念を知らなかったので勉強になりました。
    よくよく考えてみるとユーザが挿入するコードの内側と外側でOpenMPが使われている場合、一番内側のチームの番号が返ってこないと都合が悪い時が多そうです。
    そのため、一番内側のチーム内での番号が返ってくるのは理にかなってる気がします。

    キャンセル

checkベストアンサー

+1

仕様的にomp_get_thread_num()は最後のスレッド分割した際の番号を取得するものなのでしょうか?

いいえ。omp_get_thread_num()の戻り値は「該当関数を呼び出したスレッド自身の識別番号」です。

前段と後段の間でスレッドの番号の数を取得し、その数*後段のスレッド分割数をすれば計算することができるのはわかります。

残念ながら、その実装方法でしかあなたのやりたいことは実現できないと思います。

Microsoft Visual C++はOpenMP 2.0仕様までしかサポートしないため、後述するOpenMPプラグマ記述を使えません。このomp parallel for collapse(N)記法にはOpenMP 3.0仕様以降のサポートが必要です。

GCCでの実行結果: http://coliru.stacked-crooked.com/a/0e8a4e1c5d8c7cbd

// OpenMP 3.0以降の仕様準拠したC++コンパイラ(GCC, Clangなど)
#include <omp.h>
#include <iostream>

int main()
{
  // 実行環境によっては明示指定不要
  omp_set_num_threads(12);

#pragma omp parallel for collapse(2)
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 6; j++)
    {
#pragma omp critical 
      {
        std::cout << i << "," << j << " #" << omp_get_thread_num() << std::endl;
      }
    }
  }
}

安直なWorkaroundコードを書いておきます。

MSVCでの実行結果: http://rextester.com/CLLT45018

// OpenMP 2.0仕様準拠コンパイラ向け(MSVC)
#include <omp.h>
#include <iostream>

int main()
{
  // 実行環境によっては明示指定不要
  omp_set_num_threads(12);

#pragma omp parallel for
  for (int ij = 0; ij < 2*6; ij++)
  {
    int i = ij / 6, j = ij % 6;
#pragma omp critical 
    {
      std::cout << i << "," << j << " #" << omp_get_thread_num() << std::endl;
    }
  }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/07 16:41

    OpenMP2.0では無理なんですね。
    仕方ないのでboostでスレッドのIDを取り出してそこから番号を変換かけるようにしました。
    ありがとうございます。

    キャンセル

  • 2018/06/07 16:58

    ユースケース次第ですからBoost利用も悪くないと思いますが、参考用にOpenMP2.0範囲内での対策コードを追記しました。

    キャンセル

  • 2018/06/07 18:09

    ありがとうございます。
    参考にさせていただきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C++

    3791questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。