簡易的程式平行化-OpenMP(三)範例 parallel、section

本文原發表於:http://heresy.spaces.live.com/blog/cns!E0070FB8ECF9015F!1281.entry


OpenMP 裡,平行化的方式有三種:parallelsectionsfor(不過 section 和 for 都需要 parrallel)。這裡,舉些例子來說明他們的運作。

而用來測試的函式 Test內容如下

void Test( int n )
{
printf( "<T:%d> - %d
", omp_get_thread_num(), n );
}

輸出的形式會是:<T:thread_id> – n


parrallel

parrallel 的語法很直接,就是 #pragma omp parallel;不過原則上,後面要用 {} 來指定 scope。範例程式如下:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
#pragma omp parallel
{
Test( 0 );
}
system( "pause" );
}

而這樣的程式在一台雙核心的電腦上,結果應該會是:

<t:0> - 0
<t:1> - 0

從結果可以看出來,Test() 被兩個不同的 thread 個別執行了一次,所以會輸出兩行;這是因為 OpenMP 會根據硬體,自動選擇預設的執行緒數目。

接著,在針對程式些修改,變成

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#define OMP 11

int main(int argc, char* argv[])
{
#pragma omp parallel if(OMP>10) num_threads(3)
{
Test( 0 );
}
printf( "
==========================

" );
system( "pause" );
}

而這樣的程式在一台雙核心的電腦上,結果應該會是:

<t:0> - 0
<t:2> - 0
<t:1> - 0

在程式中,加入了 ifnum_threads 這兩個語法。num_threads 是用來指定執行緒的數目的,而在上面的程式中,把它指定成 3,所以結果會由三個不同的 thread,個別呼叫一次 Test()

if(OMP>10) 則是拿來控制是否要平行化的條件;如果把 #define OMP 11 改成 #define OMP 9 (或者任何不大於 10 的數)的話,結果就會變成 Test() 只被呼叫一次,只印出一行。不過要注意的一點是,用 if 條件來停止平行化,實際上應該是將執行緒的數目設定成 1;也就是說 OpenMP 還是會做處理,但是會只用一個執行緒跑。而這樣使用要注意的就是,如果曾經有因為 if 而停止平行化的話,接下來的預設的執行緒數目也會變成 1!所以如果真的要用 if 來判斷是否要平行化,最好把接下來的部份,加上 num_threads 來指定執行緒的數目。下面是一個例子:

#pragma omp parallel
Test( 1 );
#pragma omp parallel if(false)
Test( 2 );
#pragma omp parallel
Test( 3 );

他的執行結果應該會是:

<t:0> - 1
<t:1> - 1
<t:0> - 2
<t:0> - 3

這是因為在執行 Test(1) 的時候,有被平行化成兩個 thread,所以被執行了兩次。但是到了 Test(2) 的時候,因為執行了 if(false),所以將平行化關閉、設定為一個 thread;同時這也導致了接下來的 Test(3) 也變成只以一個 thread 來進行。

而在 parallel 的範圍內,還有一些 directive 是可以使用的;像是 singlemaster 等等。像下面的程式

#pragma omp parallel num_threads(2)
{
for( int i = 0; i < 3; i )
Test( i );
printf( "Hi
" );
#pragma omp single
{
printf( "Hi, single
" );
}
#pragma omp master
printf( "Hi, master
" );
}

執行結果:

<t:0> - 0
<t:1> - 0
<t:0> - 1
<t:1> - 1
<t:0> - 2
<t:1> - 2
Hi
Hi
Hi, single
Hi, master

其中,可以發現加上 singlemaster 的部份的程式只會被執行一次;而 mastersingle 兩者間的差異,則是 master 會一定由主執行緒來執行,single 不一定。

基本上,Heresy 不大確定什麼時候會直接用到 parrallel,所以也就不多加著墨了。(什麼時候會要把一般的程式多執行幾次啊? @@)


sections

sections 的用處,是把程式中沒有相關性的各個程式利用 #pragma omp section 來做區塊切割,然後由 OpenMP 做平行的處理。下面的程式是一個簡單的例子:

int main(int argc, char* argv[])
{
#pragma omp parallel sections
{
#pragma omp section
{
for( int k = 0; k < 100000; k )
{}
Test( 0 );
}
#pragma omp section
{
Test( 1 );
}
#pragma omp section
{
Test( 2 );
}
#pragma omp section
{
Test( 3 );
}
}
}

而執行出來的結果,則是:

<T:1> - 1
<T:1> - 2
<T:1> - 3
<T:0> - 0

從執行的輸出結果可以發現:由於 thread0 先執行了執行時間最久的第一個 section,而在 thread0 結束第一個 section 前,其他三個 section 已經由 thread1 執行結束了∼

不過利用 sections 平行化的時候,要注意程式的相依性;如果兩段程式是有相關性的話,實際上並不適合用 sections 來做平行化。下面是個錯誤的例子:

int	a[5];
#pragma omp parallel sections
{
#pragma omp section
{
int k;
for( int i = 0; i < 5; i )
{
a[i] = i;
for( k = 0; k < 10000; k )
{}
}
}
#pragma omp section
{
for( int i = 0; i < 5; i )
printf( "%d
", a[i] );
}
}

其中,程式裡 for( k = 0; k < 10000; k ){} 的目的只是在拖慢時間。輸出結果是:

0
1
-858993460
-858993460
-858993460

這是因為第一個 section 的部份,執行的速度比較慢,所以當第二的 section 要列印的時候,還來不及將資料填入 a[] 裡,所以會導致錯誤。


目錄:

參考資料:

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。