blog

OpenMP生成スレッドにおけるロックとアトミック操作の性能比較

マルチコアCPUのロック競合による性能劣化はどのようなものか?これは多くの人が知りたいことだと思いますので、マルチコアCPUのアトミック演算、ウィンドウズ、OpenMPのロック演算機能をテストするテス...

Apr 24, 2016 · 7 min. read
シェア

マルチコアCPUのロック競合による性能劣化はどのようなものか?多くの人が知りたがっていることだと思いますので、マルチコアCPUでのアトミック演算、windowsのCriticalSection、OpenMPのロック演算機能の性能をテストするテストプログラムを書いてみました。

InterlockedIncrementは、アトミック操作のテストに選ばれました。

各ロックとアトミック操作について、サブロックとアンロック操作のシングルタスク実行とマルチタスク実行で消費される時間をテストします。

テストの詳細なコードは以下の通り。

テストマシン環境:Intel 2.66GデュアルコアCPUマシン a

テスト走行の結果は以下の通り:

マルチスレッド、Critical_Section、2,000,000:a = 2000000、time = 5136

シングルスレッド,omp_lock 2,000,000:a = 2000000, time = 052

マルチスレッド,omp_lock 2,000,000:a = 2000000, time = 6013

シングルタスク実行の場合の消費時間は以下の通り:

アトミック動作78ms

Windows CriticalSection 172ms

OpenMPロック動作250ms

したがって、シングルタスクの状況から見ると、アトミック演算が最も速く、WindowsのCriticalSectionが2番目に速く、OpenMPライブラリに付属するロックが最も遅いのですが、これらの演算の時間差はそれほど大きくなく、ロックを使った演算はアトミック演算の2~3倍程度です。

複数のタスクが実行されている場合、消費される時間は以下のようになります:

アトミック動作156ms

Windows CriticalSection 3156ms

OpenMPロック動作1063ms

マルチタスク動作の場合、状況は予想外に変わり、アトミック動作時間はシングルタスク動作の2倍遅く、2つのCPUで動作させた場合、シングルCPUで動作させた場合の2倍遅いというのは想像を絶するもので、これはタスク切り替えのオーバーヘッドが原因と推測されます。

WindowsのCriticalSectionはさらにとんでもない結果で、実際にかかった時間は3,156ms。シングルタスクで実行した場合の18倍以上もかかっており、想像を絶する遅さです。

OpenMPのロック操作は、WindowsのCriticalSectionよりもわずかに優れていましたが、シングルタスクの約7倍である1063msもかかりました。

このことから、マルチコア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; 
} 
Read next

Windows RTの解凍ヘルパー「Unpacker」にはまだ改善の余地がある

Windows RT/8から、マイクロソフトはISOイメージをロードする機能を追加し、ユーザーはZipファイルを直接開くことができます。しかし、実際の生活では、圧縮する時、WinRARの*.rar形式を使う方が多いです。Windows RTユーザーが従来のデスクトップでRAR形式のファイルに遭遇した場合、無力でしかありません。WindowsストアにはUnpackerというメトロアプリがあります。

Apr 23, 2016 · 3 min read