开发者

Elegantly initializing openmp threads in parallel for loop

I have a for loop that uses a (somewhat complicated) counter object sp_ct to initialize an array. The serial code looks like

sp_ct.depos(0);
for(int p=0;p<size; p++, sp_ct.increment() ) {
  in开发者_JAVA技巧[p]=sp_ct.parable_at_basis();
}

My counter supports parallelization because it can be initialized to the state after p increments, leading to the following working code-fragment:

  int firstloop=-1;
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p=0;p<size;p++) {
    if( firstloop == -1 ) {
      sp_ct.depos(p); firstloop=0;
    } else { 
      sp_ct.increment();
    }
    in[p]=sp_ct.parable_at_basis();
  } // end omp paralell for

I dislike this because of the clutter that obscures what is really going on, and because it has an unnecessary branch inside the loop (Yes, I know that this is likely to not have a measurable influence on running time because it is so predictable...).

I would prefer to write something like

#pragma omp parallel for default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p=0;p<size;p++) {
#prgma omp initialize // or something
    {  sp_ct.depos(p); }
    in[p]=sp_ct.parable_at_basis();
    sp_ct.increment();
    }
  } // end omp paralell for

Is this possible?


If I generalize you problem, the question is "How to execute some intialization code for each thread of a parallel section ?", is that right ? You may use a property of the firstprivate clause : "the initialization or construction of the given variable happens as if it were done once per thread, prior to the thread's execution of the construct".

struct thread_initializer
{
  explicit thread_initializer(
    int size /*initialization params*/) : size_(size) {}

  //Copy constructor that does the init
  thread_initializer(thread_initializer& _it) : size_(_it.size)
  {
    //Here goes once per thread initialization
    for(int p=0;p<size;p++)
      sp_ct.depos(p);
  }

  int size_;
  scp_type sp_ct;
};

Then the loop may be written :

thread_initializer init(size);
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(init)
for(int p=0;p<size;p++) {
  init.sp_ct.increment();
}
in[p]=init.sp_ct.parable_at_basis();

The bad things are that you have to write this extra initializer and some code is moved away from its actual execution point. The good thing is that you can reuse it as well as the cleaner loop syntaxe.


From what I can tell you can do this by manually defining the chunks. This looks somewhat like something I was trying to do with induction in OpenMP Induction with OpenMP: getting range values for a parallized for loop in OpenMP

So you probably want something like this:

#pragma omp parallel
{
    const int nthreads = omp_get_num_threads();
    const int ithread = omp_get_thread_num();
    const int start = ithread*size/nthreads;
    const int finish = (ithread+1)*size/nthreads;       
    Counter_class_name sp_ct;

    sp_ct.depos(start);   
    for(int p=start; p<finish; p++, sp_ct.increment()) {
        in[p]=sp_ct.parable_at_basis();
    }
}

Notice that except for some declarations and changing the range values this code is almost identical to the serial code.

Also you don't have to declare anything shared or private. Everything declared inside the parallel block is private and everything declared outside is shared. You don't need firstprivate either. This makes the code cleaner and more clear (IMHO).


I see what you're trying to do, and I don't think it is possible. I'm just going to write some code that I believe would achieve the same thing, and is somewhat clean, and if you like it, sweet!

sp_ct.depos(0);
in[0]=sp_ct.parable_at_basis();
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct,firstloop)
  for(int p = 1; p < size; p++) {
    sp_ct.increment();
    in[p]=sp_ct.parable_at_basis();
  } // end omp paralell for


Riko, implement sp_ct.depos(), so it will invoke .increment() only as often as necessary to bring the counter to the passed parameter. Then you can use this code:

sp_ct.depos(0);
#pragma omp parallel for \
       default(none) shared(size,in) firstprivate(sp_ct)
for(int p=0;p<size;p++) {
  sp_ct.depos(p);
  in[p]=sp_ct.parable_at_basis();
} // end omp paralell for

This solution has one additional benefit: Your implementation only works if each thread receives only one chunk out of 0 - size. Which is the case when specifying schedule(static) omitting the chunk size (OpenMP 4.0 Specification, chapter 2.7.1, page 57). But since you did not specify a schedule the used schedule will be implementation dependent (OpenMP 4.0 Specification, chapter 2.3.2). If the implementation chooses to use dynamic or guided, threads will receive multiple chunks with gaps between them. So one thread could receive chunk 0-20 and then chunk 70-90 which will make p and sp_ct out of sync on the second chunk. The solution above is compatible to all schedules.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜