Oracle Solaris Studio 12.2: OpenMP API ユーザーガイド

5.5.3 スタックデータへの参照

タスクには、タスクコンストラクトが現れるルーチンのスタック上のデータへの参照が付加されていることがよくあります。タスクの実行は次の暗黙的または明示的バリアーまで保留されることがあるため、指定されたタスクが現れるルーチンのスタックがポップされ、スタックデータが上書きされた後に、そのタスクが実行されることがあります。そのため、タスクに共有されたということでリストアップされたスタックデータが破棄されます。

必要な同期処理を挿入し、タスクが変数を参照したときに、変数が確実にスタック上にあるようにしておくことは、プログラマの責任です。2 つの例を次に示します。

最初の例では、itask コンストラクトの中で shared に指定されています。タスクは、work() のスタック上に割り当てられている i のコピーにアクセスします。

タスクの実行は保留されることがありますので、タスクは main() の並列領域の最後の暗黙的バリアーで、work() ルーチンの処理の終了後に実行されます。そのため、タスクが i を参照すると、その時にたまたまスタック上にあった値にアクセスしてしまうことになります。

正しい結果を得るためには、プログラマはタスクが完了する前に work() を終了しないようにしておく必要があります。taskwait 指令を task コンストラクトの後に挿入することにより、この処理を追加することができます。あるいは、task コンストラクトで、i に対して shared ではなく、firstprivate を指定することもできます。


例 5–3 スタックデータ: 例 1 - 正しくないバージョン


#include <stdio.h>
#include <omp.h>
void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }


例 5–4 スタックデータ: 例 1 - 修正されたバージョン


#include <stdio.h>
#include <omp.h>

void work()
 {
   int i;

   i = 10;
   #pragma omp task shared(i)
   {
     #pragma omp critical
     printf("In Task, i = %d\n",i);
   }

   /* Use TASKWAIT for synchronization. */
   #pragma omp taskwait
 }

int main(int argc, char** argv)
 {
    omp_set_num_threads(8);
    omp_set_dynamic(0);

    #pragma omp parallel 
    {
      work();
    }
 }

この次に示す例では、task 構文中の jsections 構文中の j を参照しています。このため、タスクは firstprivate 型のコピーである sections 構文中の j にアクセスします。これは、sections 構文のアウトラインルーチンのスタック上のローカル変数です (Solaris Studio コンパイラを含む一部の実装の場合)。

タスクを sections 領域の最後の暗黙的バリアーで sections コンストラクトのアウトラインルーチン終了後に実行するため、タスクの実行は保留されることがあります。このため、タスクから j が参照されるときには、スタック上の不確定の値がアクセスされてしまいます。

正しい結果を得るためには、プログラマは sections 領域が暗黙的バリアーに達する前にタスクが実行されるようにしておく必要があります。taskwait 指令を task コンストラクトの後に挿入することにより、この処理を追加することができます。あるいは、task コンストラクトで、j に対して shared ではなく firstprivate を指定することもできます。


例 5–5 例 2 - 正しくないバージョン


#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }
          }
       }
    }

    printf("After parallel, j = %d\n",j);
 }


例 5–6 例 2 - 修正されたバージョン


#include <stdio.h>
#include <omp.h>

int main(int argc, char** argv)
 {
    omp_set_num_threads(2);
    omp_set_dynamic(0);
    int j=100;

    #pragma omp parallel shared(j)
    {
       #pragma omp sections firstprivate(j)
       {
          #pragma omp section
          {
             #pragma omp task shared(j)
             {
               #pragma omp critical
               printf("In Task, j = %d\n",j);
             }

             /* Use TASKWAIT for synchronization. */
             #pragma omp taskwait
          }
       }
    }

    printf("After parallel, j = %d\n",j);
 }