cilk

How to break out of for loop in cilk?


the for loop looks like this:

cilk_for (int i=0; i<1000000; i++){
    do something;
    if(tag == 0){
        break;
    }
}

Then when compiling, i got this error:

error: break from parallel loop is not currently supported

Solution

  • You can't break out of a cilk_for because a cilk_for does not understand order of iterations. The iterations of a parallel loop in Cilk Plus (and TBB and OpenMP and...) can execute simultaneously and/or out of order. Unless the program can predict the future, how could iteration 100 know that there was a break in iteration 50 if iteration 100 runs before or at the same time as execution 50?

    If you really need to exit the loop at iteration i before beginning iteration i+1, then your algorithm is inherently sequential and you cannot use cilk_for. If, however, bailing out of the loop is about performance (doing less work) rather than correctness, then you have a class of problem know as "speculative parallelism". In speculative parallelism, you are willing to do some extra work for the benefits of doing it in parallel, but you try to avoid doing so much extra work that the benefits of parallelism are lost.

    Cilk Plus does not have any constructs explicitly designed for speculative parallelism, but you can code some up fairly easily. The simplest thing in this case would be to make tag into an atomic variable outside the loop and change your condition to:

    if (tag == 0)
        continue;
    

    You would write to tag using sequentially-consistent memory ordering, but you can choose to read it using relaxed memory ordering to reduce memory contention. Relaxed memory ordering is usually considered in the realm of experts, but in this case, you're on pretty solid ground. A more sophisticated system would reduce memory contention further by partitioning the loop space and using a tree structure to propagate the "done" flag across iterations.

    Be aware that if you do what I suggest above, then ALL yet-to-be-completed iterations will see the change, even those that, sequentially, would have come before the iteration that sets tag to zero. If you want to stop only the subsequent iterations, then don't change tag, but use a separate atomic stop_i variable, instead, and change the logic to:

    atomic_int stop_i(1000000);
    cilk_for (int i=0; i<1000000; i++) {
        if (atomic_load(&stop_i, memory_order_relaxed) >= i)
            continue;
        do something;
        if(tag == 0){
            atomic_store(&stop_i, i, memory_order_seq_cst);
            continue;
        }
    }
    

    Note, however, that you will still get speculative execution of many iterations beyond the attempted stopping point. Only iterations that have not yet begun when you set stop_i will be affected.