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]
,並且迭代不再被很好地分配,即第一個線程比最後一個線程運行更多的迭代。
的簡單的解決方案是parellelize內環
這使用n*nthreads
關鍵操作在總共n*(n-1)/2
迭代。因此,隨着n變大,關鍵操作的成本會變得更小。您可以爲每個線程使用私有的forces
向量,並將它們合併到關鍵部分,但我不認爲這是必需的,因爲關鍵操作位於外部循環而不是內部循環。
這裏是一個解決方案,它fuses the triangular loop允許每個線程運行在相同數目的迭代。
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函數),所以我想出了基於this answer一個更簡單的解決方法。
這將三角形映射到矩形,反之亦然。首先,我將其轉換爲一個寬度爲n
但帶有n*(n-1)/2
(與三角形相同)的矩形。然後I I利用以下公式
//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];
}
*問題出現時,我看到變量,如'身體',我不知道他們來自哪裏,因爲他們以前沒有定義*。這些變量**必須先前已被定義**,否則編譯器會發出抱怨。你是代碼的主人,所以你必須知道他們來自哪裏...... – Walter
@Walter是的,但是我如何定義以前在循環內第一次出現的變量,例如body?我知道這在C中是如何工作的,因爲它非常簡單,但是對於C++我很迷茫。 –
所以你有基本的C + +的問題,並認爲這是一個好主意做併發編碼?這是行不通的。在開始考慮使用OpenMP之前,瞭解您的語言的單線程行爲是一項需求。 – Voo