データ競合の回避

フローグラフのエッジは、利用するライブラリーの依存関係を明確にします。同様に、function_node および multifunction_node オブジェクトの同時実行数の制限は、ランタイム・ライブラリーが許可する同時起動の最大数を制限します。これらの制限はライブラリーによる制限です。ライブラリーは自動的にデータ競合を防止しません。これらのメカニズムを利用して明示的にデータ競合を防ぐ必要があります。

例えば、次のコードにはノード f で参照されるグローバル・カウント・オブジェクトの同時アクセスを防ぐものが何もないため、データ競合が発生します。

  graph g;
  int src_count = 1;
  int global_sum = 0;
  int limit = 100000;

  source_node< int > src( g, [&]( int &i ) -> bool {
    if ( src_count <= limit ) {
      i = src_count++;
      return true;
    } else {
      return false;
    }
  } );

  function_node< int, int > f( g, unlimited, [&]( int i ) -> int {
    global_sum += i;  // global_sum のデータ競合
    return i; 
  } );

  make_edge( src, f );
  g.wait_for_all();

  cout << "global sum = " << global_sum 
       << " and closed form = " << limit*(limit+1)/2 << "\n";

上記の例を実行すると、データ競合により、予想よりも少ないグローバル合計が計算されるでしょう。f で許可される並列性を unlimited から 1 に変更することで、この例のデータ競合が回避され、各値が f で順番に処理されるようになります。例では、source_node がグローバルな値である src_count を更新しています。しかし、source_node は常にシリアルに実行されるため、データ競合は発生しません。

関連情報