2012-01-30 61 views
5

我有一個C代碼,計算兩組節點之間的距離(每個三個座標),即使我的代碼已經足夠快了,但我想提高它多一點使用並行計算。我已經發現了一些關於openMP的信息,我現在正在嘗試使用它,但是有些奇怪。沒有omp代碼cpu時間是20s,添加兩個編譯指示行需要160s!怎麼會發生?並行C代碼距離計算

我附上我的代碼到這裏

float computedist(float **vG1, float **vG2, int ncft, int ntri2, int jump, float *dist){ 
    int k = 0, i, j; 
    float min = 0; 
    float max = 0; 
    float avg = 0; 
    float *d = malloc(3*sizeof(float)); 
    float diff; 

    #pragma omp parallel 
    for(i=0;i<ncft;i+=jump){ 
     #pragma omp parallel 
     for(j=0;j<ntri2;j++){ 
      d[0] = vG1[i][0] - vG2[j][0]; 
      d[1] = vG1[i][1] - vG2[j][1]; 
      d[2] = vG1[i][2] - vG2[j][2]; 
      diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2)); 
      if(j==0) 
       dist[k] = diff; 
      else 
       if(diff<dist[k]) 
        dist[k] = diff; 

     } 
     avg += dist[k]; 
     if(dist[k]>max) 
      max = dist[k]; 
     k++; 
    } 

    printf("max distance: %f\n",max); 
    printf("average distance: %f\n",avg/(int)(ncft/jump)); 

    free(d); 

    return max; 
} 

非常感謝你的幫助

+0

「它怎麼會發生?」 - 通常的原因是不適當的並行化方案,無論是通過參考的地點還是太多的同步(或兩者)。 – 2012-01-30 09:11:46

+1

如果將環境變量OMP_NUM_THREADS設置爲1,並且使用單個線程運行OpenMP程序,那麼需要多少時間? – 2012-01-30 10:57:14

+0

@AlexeyKukanov可以在並行循環之前放置void omp_set_num_threads(int num_threads)嗎? – Nicholas 2012-01-30 10:59:33

回答

5

(下面的答案是指在問題的初始代碼,這是自然後用將這些建議改進)


您需要了解更多關於如何使用OpenMP的。該規範可在http://www.openmp.org;並有鏈接到教程和其他資源。

我會指出你的代碼中的一些問題,並給出如何解決這些問題的建議。

float *d = malloc(3*sizeof(float)); 
    float diff; 

d被用作臨時變量,所以應該被標記爲在#pragma omp parallel forprivate(見下文),以避免數據爭用。同時,我將使用3個獨立的浮點數來代替動態分配。 diff也包含臨時值,所以也應該是private

#pragma omp parallel 
    for(i=0;i<ncft;i+=jump){ 
     #pragma omp parallel 
     for(j=0;j<ntri2;j++){ 

您創建每個線程執行整個循環(因爲該區域不包含任何工作共享結構)並行區域,和裏面創建一個嵌套的區域與新的(!)將線程,每個執行整個內部循環。它增加了大量的開銷和不必要的計算到你的程序中。你需要的是#pragma omp parallel for,只適用於外層循環。

  d[0] = vG1[i][0] - vG2[j][0]; 
      d[1] = vG1[i][1] - vG2[j][1]; 
      d[2] = vG1[i][2] - vG2[j][2]; 
      diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2)); 

與並行性無關,但爲什麼要調用pow只是爲了計算正方形?一個好的舊乘法可能會更簡單,更快速。

  if(j==0) 
       dist[k] = diff; 
      else 
       if(diff<dist[k]) 
        dist[k] = diff; 

由於動作是相同的(dist[k]=diff;),該代碼可以由兩個條件與||(邏輯或)將被簡化。

 } 
     avg += dist[k]; 
     if(dist[k]>max) 
      max = dist[k]; 

在這裏,您可以計算外循環中的聚合值。在OpenMP中,這是通過reduction條款#pragma omp for完成的。

 k++; 
    } 

目前,您在每次迭代遞增k,從而產生導致在並行代碼的數據種族迭代之間的不必要的依賴關係。根據你的代碼,k只是i/jump的一個方便的「別名」 - 所以只需在迭代開始時將其指定爲private即可。

+0

我已經應用了所有的建議,但它仍然無法正常工作 – Nicholas 2012-01-30 10:45:24

2

你用了很多同步的,當你在兩個外環和內環添加#pragma omp parallel

使用#pragma omp parallel時,循環後有一個barrier,所以所有線程都會等到最後一個線程完成。
在你的情況下,你必須等待內循環和外循環中的所有線程,因此使用syncrhonization會產生很多開銷。

通常最好只在外環上使用#pragma omp parallel [假設有足夠的工作可以完成...]來減少障礙物的數量。

+0

如果我把#pragma omp parallel僅放在外部循環中,程序會給我總線錯誤... – Nicholas 2012-01-30 09:18:44

+0

@Nicholas:不確定,但是我認爲如果你把'private(i)'的pragma omp parallel只放在外部循環你應該沒問題。這是一個不同的問題,所以如果這個問題不起作用,你可能想發佈一個新問題,並提供關於這個問題的更多細節。 – amit 2012-01-30 09:27:26

+0

我更新了我的問題,使用私人的東西,後研究了一下:) – Nicholas 2012-01-30 10:31:24

0

在您的代碼中,您可以寫入所有線程共有的數組dist。可能你在那裏存在虛假分享問題。 嘗試使用填充分配該數組。