スケジューラーのバイパス

スケジューラーのバイパスは、次に実行するタスクを直接指定する最適化です。継続渡しスタイルでは、スケジューラーのバイパス機会が多くなります。例えば、前のセクションの継続渡しの例の最後で、execute() メソッドはタスク "a" を生成してリターンしました。「タスク・スケジュールの動作」の実行規則により、実行スレッドは次の処理を行います。

  1. タスク "a" をスレッドのデックにプッシュします。

  2. execute() メソッドからリターンします。

  3. タスク "a" をスレッドのデックからポップします (タスクが別のスレッドによってスチールされている場合を除く)。

ステップ 1 および 3 で不必要なデック操作が追加されます。また、スチールを許可することで、多くの並列処理を実装することなく、局所性が損なわれてしまいます。これらの問題は、execute() メソッドでタスクを作成する代わりに a のポインターを返すことで回避できます。「タスク・スケジュールの動作」で説明したメソッドを使用すると、a がスレッドで実行される次のタスクになります。さらに、この方法では、そのスレッドが (ほかのスレッドではなく) a を実行することが保証されます。

次の例は、前のセクションの例から必要な変更を太字で示しています。

struct FibTask: public task {
    ...
    task* execute() {
        if( n<CutOff ) {
            *sum = SerialFib(n);
            return NULL;
        } else {
            FibContinuation& c = 
                *new( allocate_continuation() ) FibContinuation(sum);
 
            FibTask& a = *new( c.allocate_child() ) FibTask(n-2,&c.x);
            FibTask& b = *new( c.allocate_child() ) FibTask(n-1,&c.y);
            // ref_count を "子タスクの数 2" に設定
            c.set_ref_count(2);
            spawn( b );
            // 変更前:	spawn( a );
            // 変更前:	return NULL;
            return &a;
        }
    }
};

関連情報