Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to parallelize this recursion using OpenMP

Tags:

c++

c

openmp

I have following recursive function (NOTE: It is stripped of all unimportant details)

int recursion(...) {

  int minimum = INFINITY;
  for(int i=0; i<C; i++) {
      int foo = recursion(...);

      if (foo < minimum) {
          minimum = foo;
      }
  }

  return minimum;
}

Note 2: It is finite, but not in this simplified example, so please ignore it. Point of this question is how to aproach this problem correctly.

I was thinking about using tasks, but I am not sure, how to use it correctly - how to paralelize the inner cycle.

EDIT 1: The recursion tree isn't well balanced. It is being used with dynamic programing approach, so as time goes on, a lot of values are re-used from previous passes. This worries me a lot and I think it will be a big bottleneck.

C is somewhere around 20.

Metric for the best is fastest :)

It will run on 2x Xeon, so there is plenty of HW power availible.

like image 905
tomascapek Avatar asked Dec 04 '16 17:12

tomascapek


1 Answers

Yes, you can use OpenMP tasks exploit parallelism on multiple recursion levels and ensure that imbalances don't cause wasted cycles.

I would collect the results in a vector and compute the minimum outside. You could also perform a guarded (critical / lock) minimum computation within the task.

Avoid spawning tasks / allocating memory for the minimum if you are too deep in the recursion, where the overhead / work ratio becomes too bad. The strongest solution it to create two separate (parallel/serial) recursive functions. That way you have zero runtime overhead once you switch to the serial function - as opposed to checking the recursion depth against a threshold every time in a unified function.

int recursion(...) {
    #pragma omp parallel
    #pragma omp single
    return recursion_par(..., 0);
}

int recursion_ser(...) {
    int minimum = INFINITY;
    for(int i=0; i<C; i++) {
        int foo = recursion_ser(...);
        if (foo < minimum) {
            minimum = foo;
        }
    }
    return minimum;
}

int recursion_par(..., int depth) {
    std::vector<int> foos(C);
    for(int i=0; i<C; i++) {
        #pragma omp task
        {
            if (depth < threshhold) {
                foos[i] = recursion_par(..., depth + 1);
            } else {
                foos[i] = recursion_ser(...);
            }
        }
    }
    #pragma omp taskwait
    return *std::min_element(std::begin(foos), std::end(foos));
}

Obviously you must not do any nasty things with global / shared state within the unimportant details.

like image 96
Zulan Avatar answered Nov 16 '22 05:11

Zulan