簡體   English   中英

OpenMP和C ++:私有變量

[英]OpenMP and C++: private variables

我對OpenMP和c ++相當陌生,也許正因為如此,我遇到了一些非常基本的問題。

我正在嘗試將所有變量都設為私有的靜態計划(以防萬一,以驗證所獲得的結果與非並行結果相同)。

當我看到諸如bodies變量時,就會出現問題,因為它們不知道它們來自何處,因為它們以前沒有定義。

是否可以將所有出現的變量(例如bodies )定義為private? 那怎么辦

  std::vector<phys_vector> forces(bodies.size());

  size_t i, j; double dist, f, alpha;


  #pragma omp parallel for schedule(static) private(i, j, dist, f, alpha)
  for (i=0; i<bodies.size(); ++i) {
    for (j = i+1; j<bodies.size(); ++j) {
      dist = distance(bodies[i], bodies[j]);
      if (dist > param.min_distance()) {
        f = attraction(bodies[i], bodies[j], param.gravity(), dist);
        alpha = angle(bodies[i],bodies[j]);
        phys_vector deltaf{ f * cos(alpha) , f * sin(alpha) };
        forces[i] += deltaf;
        forces[j] -= deltaf;
      }
    }
  }
  return forces;
}

PS:使用當前代碼,執行結果與非並行執行不同。

應該重申的是,您的bodies變量不僅會隨機出現。 您應該確切地知道它的聲明位置和定義。 但是,因為你只是訪問的元素bodies ,從來沒有改變他們,這個變量應shared無論如何,所以是不是你的問題。

您的實際問題來自於forces變量。 您必須確保不同的線程不會更改相同j的變量forces[j] 如果遵循循環的邏輯,則可以確保僅由不同的線程訪問forces[i] ,因此它們之間沒有爭用。 但是,可以通過並行i循環的不同迭代很容易地修改相同j forces[j] 您需要做的是通過遵循該StackOverflow鏈接中的答案之一來減少陣列的數量。

NoseKnowsAll已正確識別您的問題。

我想詳細解釋為什么發生此問題。 您可以使用像這樣的方形循環來完成此操作:

#pragma omp parallel for
for(int i=0; i<n; i++) {
    if(i==j) continue;
    phys_vector sum = 0;
    for(int j=0; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
    }
    forces[i] = sum;
}

它使用n*(n-1)迭代,並且易於並行化。

但是由於force(i,j) = -force(j,i)我們可以使用三角形循環(這是您所做的)在一半的迭代n*(n-1)/2完成此操作:

for(int i=0; i<n; i++) {
    phys_vector sum = 0;
    for(int j=i+1; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
        forces[j] -= deltaf;
    }
    forces[i] = sum;
}

問題是,當您執行此優化時,它會使並行化外部循環更加困難。 存在兩個問題:寫入forces[j] ,並且迭代不再均勻分布,即第一個線程比最后一個線程運行更多的迭代。

簡單的解決方案是使內循環平行

#pragma omp parallel
for(int i=0; i<n; i++) {
    phys_vector sum = 0;
    #pragma omp for
    for(int j=i+1; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
        forces[j] -= deltaf;
    }
    #pragma omp critical
    forces[i] += sum;
}

這在總共n*(n-1)/2次迭代中使用了n*nthreads關鍵操作。 因此,隨着n變大,關鍵操作的成本變小。 您可以對每個線程使用私有forces向量,並將它們合並到關鍵部分,但是我認為這不是必需的,因為關鍵操作位於外部循環而不是內部循環。


這是融合三角形循環的解決方案,允許每個線程運行相同的迭代次數。

unsigned n = bodies.size();
unsigned r = n*(n-1)/2;
#pragma omp parallel
{
    std::vector<phys_vector> forces_local(bodies.size());
    #pragma omp for schedule(static)
    for(unsigned k=0; k<r; k++) {
        unsigned i  = (1 + sqrt(1.0+8.0*k))/2;
        unsigned j = i - k*(k-1)/2;
        //calculate deltaf
        forces_local[i] += deltaf;
        forces_local[j] -= deltaf;
    }
    #pragma omp critical
    for(unsigned i=0; i<n; i++) forces[i] += forcs_local[i];
}

我對以前的三角形融合方法不滿意(因為它需要使用浮點和sqrt函數),因此我根據此答案提出了一個更簡單的解決方案。

這會將三角形映射為矩形,反之亦然。 首先,我將其轉換為寬度為n但寬度為n*(n-1)/2 (與三角形相同)的矩形。 然后,我計算矩形的(行,列)值,然后使用以下公式映射到三角形(跳過對角線)

//i is the row, j is the column of the rectangle
if(j<=i) {
    i = n - i - 2;
    j = n - j - 1;
}

讓我們選擇一個例子。 考慮以下n=5三角形環對

(0,1), (0,2), (0,3), (0,4)
       (1,2), (1,3), (1,4)
              (2,3), (2,4)
                     (3,4)

將此映射到矩形成為

(3,4), (0,1), (0,2), (0,3), (0,4)
(2,4), (2,3), (1,2), (1,3), (1,4)

具有偶數值的三角形循環的工作方式相同,盡管可能並不那么明顯。 例如, n = 4

(0,1), (0,2), (0,3)
       (1,2), (1,3)
              (2,3)

這變成

(2,3), (0,1), (0,2), (0,3)
(1,2), (1,3)

這並非完全是矩形,但映射的作用相同。 我本來可以將其映射為

 (0,1), (0,2), (0,3)
 (2,3), (1,2), (1,3)

這是一個矩形,但隨后我需要兩個公式來計算奇數和偶數三角形的大小。

這是使用矩形到三角形映射的新代碼。

unsigned n = bodies.size();
#pragma omp parallel
{
    std::vector<phys_vector> forces_local(bodies.size());
    #pragma omp for schedule(static)
    for(unsigned k=0; k<n*(n-1)/2; k++) {
        unsigned i = k/n;
        unsigned j = k%n;
        if(j<=i) {
            i = n - i - 2;
            j = n - j - 1;
        }
        //calculate deltaf
        forces_local[i] += deltaf;
        forces_local[j] -= deltaf;
    }
    #pragma omp critical
    for(unsigned i=0; i<n; i++) forces[i] += forcs_local[i];
}

暫無
暫無

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

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