OpenMP中的數(shù)據(jù)處理子句
10.1.1 private子句
private子句用于將一個或多個變量聲明成線程私有的變量,變量聲明成私有變量后,指定每個線程都有它自己的變量私有副本,其他線程無法訪問私有副本。即使在并行區(qū)域外有同名的共享變量,共享變量在并行區(qū)域內(nèi)不起任何作用,并且并行區(qū)域內(nèi)不會操作到外面的共享變量。
private子句的用法格式如下:
private(list)
下面便是一個使用private子句的代碼例子:
- int k = 100;
- #pragma omp parallel for private(k)
- for ( k=0; k < 10; k++)
- {
- printf("k=%d/n", k);
- }
- printf("last k=%d/n", k);
- 上面程序執(zhí)行后打印的結(jié)果如下:
- k=6
- k=7
- k=8
- k=9
- k=0
- k=1
- k=2
- k=3
- k=4
- k=5
- last k=100
從打印結(jié)果可以看出,for循環(huán)前的變量k和循環(huán)區(qū)域內(nèi)的變量k其實是兩個不同的變量。
用private子句聲明的私有變量的初始值在并行區(qū)域的入口處是未定義的,它并不會繼承同名共享變量的值。
出現(xiàn)在reduction子句中的參數(shù)不能出現(xiàn)在private子句中。
10.1.2 firstprivate子句
private聲明的私有變量不能繼承同名變量的值,但實際情況中有時需要繼承原有共享變量的值,OpenMP提供了firstprivate子句來實現(xiàn)這個功能。
先看一下以下的代碼例子
- int k = 100;
- #pragma omp parallel for firstprivate(k)
- for ( i=0; i < 4; i++)
- {
- k+=i;
- printf("k=%d/n",k);
- }
- printf("last k=%d/n", k);
上面代碼執(zhí)行后打印結(jié)果如下:
k=100
k=101
k=103
k=102
從打印結(jié)果可以看出,并行區(qū)域內(nèi)的私有變量k繼承了外面共享變量k的值100作為初始值,并且在退出并行區(qū)域后,共享變量k的值保持為100未變。
10.1.3 lastprivate子句
有時在并行區(qū)域內(nèi)的私有變量的值經(jīng)過計算 后,在退出并行區(qū)域時,需要將它的值賦給同名的共享變量,前面的private和firstprivate子句在退出并行區(qū)域時都沒有將私有變量的最后取 值賦給對應(yīng)的共享變量,lastprivate子句就是用來實現(xiàn)在退出并行區(qū)域時將私有變量的值賦給共享變量。
舉個例子如下:
- int k = 100;
- #pragma omp parallel for firstprivate(k),lastprivate(k)
- for ( i=0; i < 4; i++)
- {
- k+=i;
- printf("k=%d/n",k);
- }
- printf("last k=%d/n", k);
上面代碼執(zhí)行后的打印結(jié)果如下:
- k=100
- k=101
- k=103
- k=102
- last k=103
從打印結(jié)果可以看出,退出for循環(huán)的并行區(qū)域后,共享變量k的值變成了103,而不是保持原來的100不變。
由于在并行區(qū)域內(nèi)是多個線程并行執(zhí)行的, 最后到底是將那個線程的最終計算結(jié)果賦給了對應(yīng)的共享變量呢?OpenMP規(guī)范中指出,如果是循環(huán)迭代,那么是將最后一次循環(huán)迭代中的值賦給對應(yīng)的共享變 量;如果是section構(gòu)造,那么是最后一個section語句中的值賦給對應(yīng)的共享變量。注意這里說的最后一個section是指程序語法上的最后一 個,而不是實際運行時的最后一個運行完的。
如果是類(class)類型的變量使用在 lastprivate參數(shù)中,那么使用時有些限制,需要一個可訪問的,明確的缺省構(gòu)造函數(shù),除非變量也被使用作為firstprivate子句的參數(shù); 還需要一個拷貝賦值操作符,并且這個拷貝賦值操作符對于不同對象的操作順序是未指定的,依賴于編譯器的定義。
#p#
10.1.4 threadprivate子句
threadprivate子句用來指定全局的對象被各個線程各自復(fù)制了一個私有的拷貝,即各個線程具有各自私有的全局對象。
用法如下:
- #pragma omp threadprivate(list) new-line
下面用threadprivate命令來實現(xiàn)一個各個線程私有的計數(shù)器,各個線程使用同一個函數(shù)來實現(xiàn)自己的計數(shù)。計數(shù)器代碼如下:
- int counter = 0;
- #pragma omp threadprivate(counter)
- int increment_counter()
- {
- counter++;
- return(counter);
- }
如果對于靜態(tài)變量也同樣可以使用threadprivate聲明成線程私有的,上面的counter變量如改成用static類型來實現(xiàn)時,代碼如下:
- int increment_counter2()
- {
- static int counter = 0;
- #pragma omp threadprivate(counter)
- counter++;
- return(counter);
- }
threadprivate和private的區(qū)別在于threadprivate聲明的變量通常是全局范圍內(nèi)有效的,而private聲明的變量只在它所屬的并行構(gòu)造中有效。
threadprivate的對應(yīng)只能用于copyin,copyprivate,schedule,num_threads和if子句中,不能用于任何其他子句中。
用作threadprivate的變量的地址不能是常數(shù)。
對于C++的類(class)類型變量,用作threadprivate的參數(shù)時有些限制,當(dāng)定義時帶有外部初始化時,必須具有明確的拷貝構(gòu)造函數(shù)。
對于windows系統(tǒng),threadprivate不能用于動態(tài)裝載(使用LoadLibrary裝載)的DLL中,可以用于靜態(tài)裝載的DLL中,關(guān)于windows系統(tǒng)中的更多限制,請參閱MSDN中有關(guān)threadprivate子句的幫助材料。
有關(guān)threadprivate命令的更多限制方面的信息,詳情請參閱OpenMP2.5規(guī)范。
shared子句用來聲明一個或多個變量是共享變量。
用法如下:
shared(list)
需要注意的是,在并行區(qū)域內(nèi)使用共享變量時,如果存在寫操作,必須對共享變量加以保護(hù),否則不要輕易使用共享變量,盡量將共享變量的訪問轉(zhuǎn)化為私有變量的訪問。
循環(huán)迭代變量在循環(huán)構(gòu)造區(qū)域里是私有的。聲明在循環(huán)構(gòu)造區(qū)域內(nèi)的自動變量都是私有的。
default子句用來允許用戶控制并行區(qū)域中變量的共享屬性。
用法如下:
default(shared | none)
使用shared時,缺省情況下,傳入并行區(qū)域內(nèi)的同名變量被當(dāng)作共享變量來處理,不會產(chǎn)生線程私有副本,除非使用private等子句來指定某些變量為私有的才會產(chǎn)生副本。 如果使用none作為參數(shù),那么線程中用到的變量必須顯示指定是共享的還是私有的,除了那些由明確定義的除外。
reduction子句主要用來對一個或多個參數(shù)條目指定一個操作符,每個線程將創(chuàng)建參數(shù)條目的一個私有拷貝,在區(qū)域的結(jié)束處,將用私有拷貝的值通過指定的運行符運算,原始的參數(shù)條目被運算結(jié)果的值更新。
reduction子句用法如下:
reduction(operator:list)
下表列出了可以用于reduction子句的一些操作符以及對應(yīng)私有拷貝變量缺省的初始值,私有拷貝變量的實際初始值依賴于redtucion變量的數(shù)據(jù)類型。
表10-4-1:reduction操作中各種操作符號對應(yīng)拷貝變量的缺省初始值
Operator |
Initialization value |
+ | 0 |
* |
1
|
- | 0 |
& | ~0 |
| | 0 |
^ | 0 |
&& | 1 |
|| | 0 |
例如一個整數(shù)求和的程序如下:
- int i, sum = 100;
- #pragma omp parallel for reduction(+: sum)
- for ( i = 0; i < 1000; i++ )
- {
- sum += i;
- }
- printf( "sum = %ld/n", sum);
注意,如果在并行區(qū)域內(nèi)不加鎖保護(hù)就直接對共享變量進(jìn)行寫操作,存在數(shù)據(jù)競爭問題,會導(dǎo)致不可預(yù)測的異常結(jié)果。共享數(shù)據(jù)作為private、firstprivate、lastprivate、threadprivate、reduction子句的參數(shù)進(jìn)入并行區(qū)域后,就變成線程私有了,不需要加鎖保護(hù)了。
#p#
copyin子句用來將主線程中threadprivate變量的值拷貝到執(zhí)行并行區(qū)域的各個線程的threadprivate變量中,便于線程可以訪問主線程中的變量值,
用法如下:
- copyin(list)
copyin中的參數(shù)必須被聲明成threadprivate的,對于類類型的變量,必須帶有明確的拷貝賦值操作符。
對于前面threadprivate中講過的計數(shù)器函數(shù),如果多個線程使用時,各個線程都需要對全局變量counter的副本進(jìn)行初始化,可以使用copyin子句來實現(xiàn),示例代碼如下:
- int main(int argc, char* argv[])
- {
- int iterator;
- #pragma omp parallel sections copyin(counter)
- {
- #pragma omp section
- {
- int count1;
- for ( iterator = 0; iterator < 100; iterator++ )
- {
- count1 = increment_counter();
- }
- printf("count1 = %ld/n", count1);
- }
- #pragma omp section
- {
- int count2;
- for ( iterator = 0; iterator < 200; iterator++ )
- {
- count2 = increment_counter();
- }
- printf("count2 = %ld/n", count2);
- }
- }
- printf("counter = %ld/n", counter);
- }
打印結(jié)果如下:
count1 = 100
count2 = 200
counter = 0
copyprivate子句提供了一種機(jī)制用一個私有變量將一個值從一個線程廣播到執(zhí)行同一并行區(qū)域的其他線程。
用法如下:
copyprivate(list)
copyprivate子句可以關(guān)聯(lián)single構(gòu)造,在single構(gòu)造的barrier到達(dá)之前就完成了廣播工作。copyprivate可以對private和threadprivate子句中的變量進(jìn)行操作,但是當(dāng)使用single構(gòu)造時,copyprivate的變量不能用于private和firstprivate子句中。
下面便是一個使用copyprivate的代碼例子:
- int counter = 0;
- #pragma omp threadprivate(counter)
- int increment_counter()
- {
- counter++;
- return(counter);
- }
- #pragma omp parallel
- {
- int count;
- #pragma omp single copyprivate(counter)
- {
- counter = 50;
- }
- count = increment_counter();
- printf("ThreadId: %ld, count = %ld/n", omp_get_thread_num(), count);
- }
打印結(jié)果為:
ThreadId: 2, count = 51
ThreadId: 0, count = 51
ThreadId: 3, count = 51
ThreadId: 1, count = 51
如果沒有使用copyprivate子句,那么打印結(jié)果為:
ThreadId: 2, count = 1
ThreadId: 1, count = 1
ThreadId: 0, count = 51
ThreadId: 3, count = 1
從打印結(jié)果可以看出,使用copyprivate子句后,single構(gòu)造內(nèi)給counter賦的值被廣播到了其他線程里,但沒有使用copyprivate子句時,只有一個線程獲得了single構(gòu)造內(nèi)的賦值,其他線程沒有獲取single構(gòu)造內(nèi)的賦值。
原文鏈接:http://blog.csdn.net/drzhouweiming/article/details/2033276