トークンベース・システムの作成

フローグラフのメッセージ数を制限する柔軟なソリューションは、トークンを使用することです。トークンベース・システムでは、グラフで利用可能なトークンの数が限られており、メッセージはトークンとペアになるまでグラフに入ることができません。メッセージがグラフからリタイアされると、そのトークンが解放され、新しいメッセージとペアになり、グラフに入ることができます。

tbb::pipeline および tbb::parallel_pipeline アルゴリズムはトークンベース・システムに依存します。フローグラフのインターフェイスはトークンを明示的にサポートしていませんが、join_node を使用して類似のシステムを作成できます。join_node には、2 つのテンプレート引数、入力の種類を記述するタプル、バッファーポリシーがあります。

template<typename OutputTuple, graph_buffer_policy JP = queueing>
class join_node;

バッファーポリシーは次のいずれかになります。

トークンベース・システムは、reserving join_node を使用して作成できます。

下記の例では、M ビッグ・オブジェクトを生成する source_node と、3 つのトークンがあらかじめ設定された buffer_node があります。token_t は何にでも (例えば、typedef int token_t; に) できます。source_nodebuffer_node は、reserving join_node に接続されます。reserving join_node により取得されると、source_node は入力を生成します。また、reserving join_node は、buffer_node から取得するアイテムがあることを知っている場合は、source_node から入力を取得します。

  graph g;

  int src_count = 0;
  int number_of_objects = 0;
  int max_objects = 3;

  source_node< big_object * > s( g, [&]( big_object* &v ) -> bool {
	  if ( src_count < M ) {
		v = new big_object();
        ++src_count;
        return true;
      } else {
        return false;
      }
  } );

  join_node< tuple_t, reserving > j(g);

  buffer_node< token_t > b(g);

  function_node< tuple_t, token_t > f( g, unlimited, 
    []( const tuple_t &t ) -> token_t {
        spin_for(1);
	 cout << get<1>(t) << "\n";
        delete get<0>(t);
	 return get<1>(t);
  } );

  make_edge( s, input_port<0>(j) );
  make_edge( b, input_port<1>(j) );
  make_edge( j, f );
  make_edge( f, b );

  b.try_put( 1 );
  b.try_put( 2 );
  b.try_put( 3 );

  g.wait_for_all();

上記のコードで、function_node はトークンを buffer_node に戻しています。このフローグラフのサイクルにより、トークンがリサイクルされ、source_node の別の入力とペアになることができます。そのため、前のセクションのように、グラフには最大 4 つのビッグ・オブジェクトがあります。function_node に 3 つのビッグ・オブジェクト、source_node にバッファーされる 1 つのビッグ・オブジェクトがあり、ペアになるトークンを待機します。

フローグラフ用に定義された特定の token_t はないため、オブジェクトや配列へのポインターを含む、任意の型をトークンに使用できます。そのため、上記の例とは異なり、token_t は仮の型である必要はなく、例えば、計算に不可欠なバッファーやその他のオブジェクトでもかまいません。例えば、ビッグ・オブジェクト自身をトークンとして使用するように上記の例を変更すると、ビッグ・オブジェクトの割り当てと割り当て解除の繰り返し、そして buffer_node に戻すサイクルを利用してビッグ・オブジェクトのフリーリストを作成する必要性がなくなります。

また、上記の例で、buffer_node は固定数の try_put の明示的な呼び出しによりあらかじめ設定されていますが、ほかの方法もあります。例えば、source_nodebuffer_node の入力にアタッチできます。また、トークンを生成できます。さらに、function_node は、出力ポートに 0 以上の出力を送る multifunction_node に置換できます。multifunction_node を使用して、トークンをリサイクルするかしないか、より多くのトークンを生成するかを選択し、グラフで許可される同時実行数を増減できます。

このため、トークンベース・システムは非常に柔軟です。トークンを任意の種類で宣言し、実行とともにシステムに挿入あるいはシステムから削除して、システムで許可される同時実行数を動的に制御できます。ソースでトークンを入力とペアにできるため、このアプローチにより、グラフ全体のリソース消費を制限することができます。

関連情報