簡體   English   中英

優雅地並行初始化openmp線程進行循環

[英]Elegantly initializing openmp threads in parallel for loop

我有一個for循環,它使用一個(有點復雜的)計數器對象sp_ct來初始化一個數組。 序列代碼看起來像

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

我的計數器支持並行化,因為它可以在p遞增后初始化為狀態,從而導致以下工作代碼片段:

  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

我不喜歡這個,因為雜亂的東西掩蓋了真正發生的事情,並且因為它在循環中有一個不必要的分支(是的,我知道這可能不會對運行時間產生可測量的影響,因為它是如此可預測的.. )。

我更喜歡寫類似的東西

#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

這可能嗎?

如果我概括你的問題,問題是“如何為並行部分的每個線程執行一些初始化代碼?”,是嗎? 您可以使用firstprivate子句的屬性:“給定變量的初始化或構造就像在線程執行構造之前每個線程完成一次一樣”。

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;
};

然后可以編寫循環:

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();

糟糕的是你必須編寫這個額外的初始化程序,並且一些代碼會從其實際執行點移開。 好消息是您可以重用它以及更清晰的循環語法。

據我所知,您可以通過手動定義塊來做到這一點。 這看起來有點像我正在嘗試對OpenMP中的歸納做的事情對OpenMP 歸納的了解:在OpenMP中獲取並行化for循環的范圍值

所以你可能想要這樣的東西:

#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();
    }
}

請注意,除了一些聲明和更改范圍值之外,此代碼與串行代碼幾乎相同。

另外,您不必聲明任何共享或私有的內容。 在並行塊內部聲明的所有內容都是私有的,在外部聲明的所有內容都是共享的。 你也不需要firstprivate。 這使代碼更清晰,更清晰(恕我直言)。

Riko,實現了sp_ct.depos() ,所以它只會根據需要調用.increment()來將計數器帶到傳遞的參數。 然后你可以使用這段代碼:

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

此解決方案還有一個額外的好處:只有每個線程只接收0 - size一個塊時,您的實現才有效。 在指定schedule(static)忽略塊大小的情況就是這種情況( OpenMP 4.0規范 ,第2.7.1章,第57頁)。 但是,由於您未指定schedule因此所使用的時間表將取決於實現( OpenMP 4.0規范 ,第2.3.2章)。 如果實現選擇使用dynamicguided ,則線程將接收多個塊,它們之間存在間隙。 因此,一個線程可以接收0-20塊,然后接收70-90塊,這將使psp_ct在第二個塊上不同步。 上述解決方案與所有計划兼容。

我明白你要做什么,我認為不可能。 我只是要編寫一些我認為可以實現同樣功能的代碼,而且有點干凈,如果你喜歡它,那就太好了!

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM