Recycling

Not only can you bypass the scheduler, you might also bypass task allocation and deallocation. The opportunity frequently arises for recursive tasks that do scheduler bypass. Consider the example in the Scheduler Bypass section. After it creates continuation task "c", it performs the following steps:

  1. Create child task "a".

  2. Create and spawn child task "b"

  3. Return from method execute() with pointer to task "a".

  4. Destroy parent task.

Recycling the parent as "a" can avoid the task creation destruction done by steps 1 and 4. Furthermore, in many scenarios step 1 copies state from the parent. Recycling the parent as task "a" eliminates the copying overhead.

The following code shows the changes required to implement recycling in the scheduler-bypass example.

struct FibTask: public task {
    long n;
    long* sum;
    ...
    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); This line removed
            FibTask& b = *new( c.allocate_child() ) FibTask(n-1,&c.y);
            recycle_as_child_of(c);
            n -= 2;
            sum = &c.x;
            // Set ref_count to "two children".
            c.set_ref_count(2);
            spawn( b );
            // return &a; This line removed
            return this;
        }
    }
};

The child that was previously called a is now the recycled this. The call recycle_as_child_of(c) has several effects:

  • It marks this as to not be automatically destroyed when execute() returns.

  • It sets the successor of this to be c.

To prevent reference-counting problems, recycle_as_child_of has a prerequisite that this must have a NULL successor. This is the case after allocate_continuation occurs. The following figure shows how allocate_continuation and recycle_as_child_of transform the task graph.

Action of allocate_continuation Followed By recycle_as_child_of

When recycling, ensure that the original task’s fields are not used after the task might start running. The example uses the scheduler bypass trick to ensure this. You can spawn the recycled task instead, as long as none of its fields are used after the spawning. This restriction applies even to any const fields, because after spawning the task might run and be destroyed before the parent progresses any further.

Note

A similar method, task::recycle_as_continuation() recycles a task as a continuation instead of a child.

See Also

For more complete information about compiler optimizations, see our Optimization Notice.