我在你的代碼中看到的問題是在這條線
array1[array2[j]] += z[j]+j;
這意味着array1
有可能通過取其j
指標進行修改。在功能myfunc()
的上下文中的j
對應於上層的索引i
。問題是i
是循環並行化的索引,因此,這意味着array1
可以隨時由任何線程同時修改。
現在的關鍵問題是要知道,如果array2
可以有不同的索引相同的值:
- 如果您確信不管什麼
j1 != j2
你有array2[j1] != array2[j2]
,那麼你的代碼是平凡可並行。
- 如果存在
j1 != j2
的值,並且您有array[j1] == array[j2]
,那麼您在array1
的迭代中存在依賴關係,並且代碼不再(簡單和/或有效)可並行化。
因此,讓我們假設我們是在前者的情況下,那麼你已經在代碼中有足夠的OpenMP指令:
i
需求是private
,但隱含已經如此,因爲它是該指數並行迴路的;
x
和y
應該是shared
(它們是默認值),因爲它們的訪問索引是並行分佈的(即i
),所以它們的並行更新不會重疊;
array2
只能在讀取模式下訪問,所以它是一個簡單的方法shared
(它默認是再次);
array1
array1
被讀取和寫入,但由於我們最初的假設,線程之間沒有可能的衝突,因爲它們的索引集訪問它是不相容的。因此,默認的shared
限定符可以正常工作。
但現在,如果我們的情況下array2
允許非套防脫離指數的訪問array1
,我們將不得不保留的array1
這些訪問/更新的順序。這可以通過ordered
子句/指令完成。由於我們仍然希望並行化(某種程度上)有效,因此我們必須在parallel
指令中添加schedule(static,1)
子句。欲瞭解更多詳情,請參閱this great answer。現在您的代碼應該是這樣的:
//global variables
static int array1[100000];
static int array2[100000];
//part of program code from one of function.
int i
int x[1000000];
int y[1000000];
#pragma omp parallel for schedule(static,1) ordered
for(i=0; i<100; i++)
{
y[i] = i*i-3*i-10*random();
x[i] = myfunc(i, y[i])
}
//additional function
int myfunc(j, z)
int j,
int z[]
{
int tmp = z[j]+j;
#pragma omp ordered
array1[array2[j]] += tmp;
return array1[j];
}
這(我認爲)的工作,並在並行不算太差(線程數量有限)的期限,但是這有一個大(巨大)的缺陷:它會在更新x
和y
時生成大量的false sharing。因此,使用這些代碼的每個線程副本並僅在最後更新全局數組可能更有優勢。然後代碼段的中心部分會是這個樣子(在所有未測試):
#pragma omp parallel
#pragma omp single
int nbth = omp_get_num_threads();
int *xm = malloc(1000000*nbth*sizeof(int));
int *ym = malloc(1000000*nbth*sizeof(int));
#pragma omp parallel
{
int tid = omp_get_thread_num();
int *xx = xm+1000000*tid;
int *yy = ym+1000000*tid;
#pragma omp for schedule(static,1) ordered
for(i=0; i<100; i++)
{
yy[i] = i*i-3*i-10*random();
xx[i] = myfunc(i, y[i])
}
#pragma omp for
for (i=0; i<100; i++)
{
int j;
x[i] = 0;
y[i] = 0;
for (j=0; j<nbth; j++)
{
x[i] += xm[j*1000000+i];
y[i] += ym[j*1000000+i];
}
}
}
free(xm);
free(ym);
這將避免錯誤共享,但會增加存儲器訪問的次數和並行化的開銷。畢竟,這可能不是很有利。你必須親自在你的實際代碼中看到它。
BTW,當相應的陣列被宣佈爲百萬久,i
只循環,直到100的事實看起來可疑我。如果100是真正的循環正確的尺寸,則可能是並行化是不值得的呢?
編輯:
吉姆Cownie的評論指出了這一點,我錯過了對random()
的調用,作爲跨迭代的依賴源,阻止了正確的並行化。我不確定這與你的實際代碼有多相關(我懷疑你真的用隨機數據填充你的y
數組),但如果你這樣做,你必須改變這部分,以便並行地完成(否則,生成隨機數序列所需的序列化只會殺死來自並行化的任何增益)。但是並行產生非相關的僞隨機序列並不像聽起來那麼簡單。您可以使用rand_r()
而不是random()
作爲RNG的線程安全替代方法,並將其每個線程的種子初始化爲不同的值。然而,你不確定一個線程的系列會不會很快與另一個線程的系列產生衝突(一段時間後線程開始產生一個系列而不是另一個系列,從而擾亂了預期的漸近行爲)。
因爲我很確定你對此沒有真正的興趣,所以我不會再發展(這本身就是一個完整的問題),但我只會用(不太好)rand_r()
把戲。如果你想要得到更多關於生成優秀並行隨機序列的可能替代方案的細節,只需要問另一個問題。
在沒有問題來自array2
(防脫離套指標)的情況下,代碼將成爲:
// global variable
unsigned int seed;
#pragma omp threadprivate(seed)
// done just once somewhere
#pragma omp parallel
seed = omp_get_thread_num(); //or something else, but different for each thread
// then the parallelised loop
#pragma omp parallel for
for(i=0; i<100; i++)
{
y[i] = i*i-3*i-10*rand_r(&seed);
x[i] = myfunc(i, y[i])
}
那麼其他案件都使用同樣的伎倆,除了那些已經被描述。但是,請記住,這對於嚴重的基於RNG的計算(如蒙特卡羅方法)來說不夠好。如果你想要的只是爲了測試目的而產生一些值,但它不會通過任何嚴肅的統計質量測試。
你能否請你清理一下你的代碼片段。我只是不明白什麼是全球性的,這個''for''循環在什麼地方不做,等等。你是否認真地使用K&R風格函數聲明? – Gilles
這個庫的所有者使用的K&R樣式函數。我只是想修改源代碼。提供的代碼就是例子。我想用這個例子來理解選擇正確的變量類型的規則:shared,private或threadprivate。 –
除了[div](https:// github。)等古代功能外,還有哪些人仍在使用K&R C。COM /賽靈思/ eglibc/BLOB /主/ STDLIB/div.c)? –