OpenMP創建線程中的鎖及原子操作性能比較
在多核CPU中鎖競爭到底會造成性能怎樣的下降呢?相信這是許多人想了解的,因此特地寫了一個測試程序來測試原子操作,windows CriticalSection, OpenMP的鎖操作函數在多核CPU中的性能。
原子操作選用InterlockedIncrement來進行測試,
對每種鎖和原子操作,都測試在單任務執行和多任務執行2000000次加鎖解鎖操作所消耗的時間。
測試的詳細代碼見后面。
測試機器環境: Intel 2.66G 雙核CPU 機器一臺
測試運行結果如下:
SingleThread, InterlockedIncrement 2,000,000: a = 2000000, time = 78
MultiThread, InterlockedIncrement 2,000,000: a = 2000000, time = 156
SingleThread, Critical_Section 2,000,000:a = 2000000, time = 172
MultiThread, Critical_Section, 2,000,000:a = 2000000, time = 3156
SingleThread,omp_lock 2,000,000:a = 2000000, time = 250
MultiThread,omp_lock 2,000,000:a = 2000000, time = 1063
在單任務運行情況下,所消耗的時間如下:
原子操作 78ms
Windows CriticalSection 172ms
OpenMP 的lock操作 250ms
因此從單任務情況來看,原子操作最快,Windows CriticalSection次之,OpenMP庫帶的鎖最慢,但這幾種操作的時間差距不是很大,用鎖操作比原子操作慢了2~3倍左右。
在多個任務運行的情況下,所消耗的時間如下:
原子操作 156ms
Windows CriticalSection 3156ms
OpenMP 的lock操作 1063ms
在多任務運行情況下,情況發生了意想不到的變化,原子操作時間比單任務操作時慢了一倍,在兩個CPU上運行比在單個CPU上運行還慢一倍,真是難以想象,估計是任務切換開銷造成的。
Windows CriticalSection則更離譜了,居然花了3156ms,是單任務運行時的18倍多的時間,慢得簡直無法想象。
OpenMP的lock操作比Windows CriticalSection稍微好一些,但也花了1063ms,是單任務時的7倍左右。
由此可以知道,在多核CPU的多任務環境中,原子操作是最快的,而OpenMP次之,Windows CriticalSection則最慢。
同時從這些鎖在單任務和多任務下的性能差距可以看出,,多核CPU上的編程和以往的單核多任務編程會有很大的區別。
需要說明的是,本測試是一種極端情況下的測試,鎖住的操作只是一個簡單的加1操作,并且鎖競爭次數達200萬次之多,在實際情況中,一由于任務中還有很多不需要加鎖的代碼在運行,實際情況中的性能會比本測試的性能好很多。
測試代碼如下:
- // TestLock.cpp : OpenMP任務中的原子操作和鎖性能測試程序。
- //
- #include <windows.h>
- #include <time.h>
- #include <process.h>
- #include <omp.h>
- #include <stdio.h>
- void TestAtomic()
- {
- clock_t t1,t2;
- int i = 0;
- volatile LONG a = 0;
- t1 = clock();
- for( i = 0; i < 2000000; i++ )
- {
- InterlockedIncrement( &a);
- }
- t2 = clock();
- printf("SingleThread, InterlockedIncrement 2,000,000: a = %ld, time = %ld/n", a, t2-t1);
- t1 = clock();
- #pragma omp parallel for
- for( i = 0; i < 2000000; i++ )
- {
- InterlockedIncrement( &a);
- }
- t2 = clock();
- printf("MultiThread, InterlockedIncrement 2,000,000: a = %ld, time = %ld/n", a, t2-t1);
- }
- void TestOmpLock()
- {
- clock_t t1,t2;
- int i;
- int a = 0;
- omp_lock_t mylock;
- omp_init_lock(&mylock);
- t1 = clock();
- for( i = 0; i < 2000000; i++ )
- {
- omp_set_lock(&mylock);
- a+=1;
- omp_unset_lock(&mylock);
- }
- t2 = clock();
- printf("SingleThread,omp_lock 2,000,000:a = %ld, time = %ld/n", a, t2-t1);
- t1 = clock();
- #pragma omp parallel for
- for( i = 0; i < 2000000; i++ )
- {
- omp_set_lock(&mylock);
- a+=1;
- omp_unset_lock(&mylock);
- }
- t2 = clock();
- printf("MultiThread,omp_lock 2,000,000:a = %ld, time = %ld/n", a, t2-t1);
- omp_destroy_lock(&mylock);
- }
- void TestCriticalSection()
- {
- clock_t t1,t2;
- int i;
- int a = 0;
- CRITICAL_SECTION cs;
- InitializeCriticalSection(&cs);
- t1 = clock();
- for( i = 0; i < 2000000; i++ )
- {
- EnterCriticalSection(&cs);
- a+=1;
- LeaveCriticalSection(&cs);
- }
- t2 = clock();
- printf("SingleThread, Critical_Section 2,000,000:a = %ld, time = %ld/n", a, t2-t1);
- t1 = clock();
- #pragma omp parallel for
- for( i = 0; i < 2000000; i++ )
- {
- EnterCriticalSection(&cs);
- a+=1;
- LeaveCriticalSection(&cs);
- }
- t2 = clock();
- printf("MultiThread, Critical_Section, 2,000,000:a = %ld, time = %ld/n", a, t2-t1);
- DeleteCriticalSection(&cs);
- }
- int main(int argc, char* argv[])
- {
- TestAtomic();
- TestCriticalSection();
- TestOmpLock();
- return 0;
- }
原文鏈接:http://blog.csdn.net/drzhouweiming/article/details/1689853