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


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

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

void Test( int n )
{
  printf( "<T:%d> - %dn", 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( "n==========================nn" );
	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( "Hin" );
	#pragma omp single
	{
		printf( "Hi, singlen" );
	}
	#pragma omp master
	printf( "Hi, mastern" );
}

執行結果:

<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

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

不過利用 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( "%dn", a[i] );
	}
}

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

0
1
-858993460
-858993460
-858993460

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

參考資料:


目錄:

對「簡易的程式平行化-OpenMP(三)範例 parallel、section」的想法

  1. 您好,
    我是用 mingw + gcc 來使用 openmp,我製作了一個 dll,在 dll 內只有一個下面的 function

    DLL_EXPORT void __stdcall Test(Byte *Source, int size)
    {
    #pragma omp parallel for
    for(int i=0; i<size; i++)
    {
    if(Source[i] == 5)
    Source[i] = 22;
    }
    }

    我用 bcb 寫了個測試程式,測試如下:
    主程式執行 Test() ,功能正常
    主程式內新增一個 Thread,在 Thread 內 執行 Test() ,這時候就一定會當掉

    openmp 不能在 Thread 內呼叫嗎?
    謝謝您

    • 您好,Heresy 自己沒有用過使用過 mingw + gcc 的環境,也沒有把程式封包成 DLL 的經驗。
      不過在 MSVC 和 GCC 下,應該都是可以在 pThread 開啟新的 thread 裡面,呼叫有使用 OpenMP 的程式的,這種寫法 Heresy 這邊目前有在使用,應該沒問題才對。

  2. 您好又確認了一下,看來應該是以前測試的時候不知道哪裡出問題了…的確在執行#pragma omp parallelTest( 3 );時還是會平行化進行的。文章內容有問題的部分也先抽掉了。

  3. 我在執行下面的範例時, Test(3)的結果沒有因為Test( 2 )關閉平行化而只有一個thread,他還是多個thread在執行??#pragma omp parallelTest( 1 );#pragma omp parallel if(false)Test( 2 );#pragma omp parallelTest( 3 );

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料