私は最近、非常に計算コストの高い画像解析プロジェクトに取り組む予定であるため、OpenMP の調査を開始しました。Intel i7 (8 コア) と mingw64 gcc 4.8.1 で Windows 7 を使用しています。Code::Blocks でコーディングし、コンパイルして実行するためにすべてをセットアップします。コードのいくつかの部分で、並列処理に適していると思われるピクセル単位の操作を実行します。驚いたことに、逐次処理は並列処理よりも高速であることがわかりました。32 ビットと 64 ビットの両方で、2 台の別々のコンピューターで異なるバージョンの gcc (4.7 - 4.8) を試しましたが、常に同じパフォーマンスの問題が発生します。次に、これら 2 台のコンピューターのいずれかで使用していた古い Visual Studio 2008 で実行しようとしましたが、期待どおりにパフォーマンスが向上しました。したがって、私の質問は、gcc を使用して同じ効果を確認できないのはなぜですか。
これは最小限の作業例です。
#include <omp.h>
#include <cstdlib>
#include <iostream>
int main(int argc, char * argv[])
{
/* process a stack of images - set the number to 1000 for testing */
int imgStack = 1000;
double start_t = omp_get_wtime();
for (int img = 0; img < imgStack; img++)
{
omp_set_num_threads(8);
#pragma omp parallel for default(none)
for (int y = 0; y < 1000000000; y++) /* increased the number of pixels to make it worthwhile and to see a difference*/
{
for (int x = 0; x < 1000000000; x++)
{
unsigned char pixel[4];
pixel[0] = 1;
pixel[1] = 2;
pixel[2] = 3;
pixel[3] = 4;
/* here I would do much more but removed it for testing purposes */
}
}
}
double end_t = (omp_get_wtime() - start_t) * 1000.0;
std::cout << end_t << "ms" << std::endl;
return 0;
}
建物のログには次のものがあります
x86_64-w64-mingw32-g++.exe -Wall -O2 -fopenmp -c C:\Code\omptest\main.cpp -o obj\Release\main.o
x86_64-w64-mingw32-g++.exe -o bin\Release\omptest.exe obj\Release\main.o -s C:\mingw-builds\x64-4.8.1-posix-seh-rev5\mingw64\bin\libgomp-1.dll
出力は次のとおりです
for 1 thread : 43ms
for 8 threads: 594ms
また、コンパイラがループ展開を行う場合に備えて、最適化 (-O0) をオフにしようとしました。誤った共有の問題について読んだので、ループ内の変数を非公開にして、これが問題ではないことを確認しました。私はプロファイリングが得意ではないので、すべてのスレッドを待機させる内部ロックなど、その下で何が起こっているのかわかりません。
ここで何が間違っているのかわかりません。
- 編集 -
みんなありがとう。私の実際のコードでは、それぞれ 2000x2000 ピクセル サイズの 2000 個の画像を含む画像スタックがあります。誰もが問題を簡単に再現できるように例を単純化しようとしましたが、単純化しすぎて他の問題が発生しました。あなたはすべて完全に正しかった。私の実際のコードでは、画像を開いて表示するために Qt を使用しています。また、スタックを読み込んで反復処理し、一度に 1 つの画像を提供する独自の画像マネージャーも使用しています。サンプル全体を提供するのは多すぎて複雑になると思いました (つまり、最小限の実用的な例を提供していません)。
すべての変数 (imageHeight、imageWidth など) を const として渡すだけで、イメージへのポインターは共有されます。最初は QImage へのポインタでした。ループでは、qtimg->setPixel(...) を使用して最終的なピクセル値を設定しましたが、MSVC コンパイラは gcc コンパイラとは異なる方法で処理しているようです。最後に、QImage ポインターを unsigned char 配列へのポインターに置き換えました。これにより、期待どおりにパフォーマンスが向上しました。
@Hristo Iliev: スレッドプールに関する情報をありがとう。それを知って本当に良かったです。