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

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

フローグラフ内のメッセージ数を制限するより柔軟な解決策として、トークンを使用する方法があります。トークンベースのシステムでは、グラフ内で使用できるトークンの数は限られており、メッセージは、使用可能なトークンとペアになるまでグラフに入ることができません。メッセージがグラフから削除されると、そのトークンが解放され、新しいメッセージとペアになって、入力が許可されます。

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

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

バッファーポリシーは以下のいずれかです。

  • queueing。このタイプのポリシーでは、入力が先入先出 (FIFO) 方式で照合されます。つまり、入力は受信順に結合されてタプルが形成されます。

  • tag_matching。このタイプのポリシーは、一致するタグを持つ入力を結合します。

  • reserving。このタイプのポリシーにより、join_node は内部的にバッファリングを行わず、上流ソースから各ポートの入力を最初に予約できる場合にのみ入力を消費します。各ポートで入力を予約できる場合は、それらの入力を取得し、それらを結合して出力タプルを形成します。

予約 join_nodes を使用することで、トークンベースのシステムを作成できます。

以下の例では、M 個の大きなオブジェクトを生成する input_node と、3 つのトークンが事前入力される buffer_node があります。token_t は任意の値にすることができます。例えば、typedef int token_t; などです。input_nodebuffer_node は予約 join_node に接続されます。input_node は、予約 join_node によって入力がプルされた場合にのみ入力を生成し、予約 join_node は、buffer_node からプルする項目もあることを認識している場合にのみ、input_node から入力をプルします。

graph g; 

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

input_node< big_object * > s( g, [&]( oneapi::tbb::flow_control& fc ) -> big_object* { 
    if ( src_count < M ) { 
        big_object* v = new big_object(); 
        ++src_count; 
        return v; 
    } else { 
        fc.stop(); 
        return nullptr; 
    } 
} ); 
s.activate(); 

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 に返すことがわかります。フローグラフのこのサイクルでは、トークンを再利用して input_node からの別の入力とペアにできます。したがって、前のセクションと同様に、グラフには最大 4 つの大きなオブジェクトが存在します。function_node には 3 つの大きなオブジェクトがあり、input_node には 1 つのオブジェクトがバッファリングされ、トークンとペアになるのを待機しています。

フローグラフには特定の token_t が定義されていないため、オブジェクトや配列へのポインターなど、任意のタイプのトークンを使用できます。したがって、上記の例とは異なり、token_t はダミータイプである必要はなく、例えば計算に不可欠なバッファーやその他のオブジェクトにできます。例えば、上記の例を変更して、大きなオブジェクト自体をトークンとして使用すると、割り当てと割り当て解除を繰り返し行う必要がなくなり、基本的に buffer_node へのサイクルバックを使用して大きな空きリストを作成できます。

また、上記の例では、buffer_nodetry_put への明示的な呼び出しの固定数によって事前に入力されましたが、他のオプションもあります。例えば、input_nodebuffer_node の入力に接続して、トークンを生成できます。さらに、function_node は、オプションで各出力ポートに 0 個以上の出力を配置できる multifunction_node に置き換えることもできます。multifunction_node を使用すると、トークンを再利用するか選択したり、さらにトークンを生成したりして、グラフ内で許可される同時実行性を増減できます。

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