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