私は先週レイトレーサーを書いていましたが、マルチスレッドが理にかなっているほど十分に機能しているところまで来ました。OpenMPを使用して並列化しようとしましたが、より多くのスレッドで実行すると、実際には1つで実行するよりも遅くなります。
特にOpenMPに関する他の同様の質問を読んで、1つの提案は、gccがシリアルコードをより適切に最適化することでした。ただし、以下のコンパイル済みコードの実行は、の場合のexport OMP_NUM_THREADS=1
2倍の速度export OMP_NUM_THREADS=4
です。つまり、両方の実行で同じコンパイル済みコードです。
プログラムの実行time
:
> export OMP_NUM_THREADS=1; time ./raytracer
real 0m34.344s
user 0m34.310s
sys 0m0.008s
> export OMP_NUM_THREADS=4; time ./raytracer
real 0m53.189s
user 0m20.677s
sys 0m0.096s
ユーザー時間は実際よりもはるかに短くなります。これは、複数のコアを使用する場合は珍しいことです。複数のコアが同時に実行されているため、ユーザーは実際よりも長くする必要があります。
OpenMPを使用して並列化したコード
void Raytracer::render( Camera& cam ) {
// let the camera know to use this raytracer for probing the scene
cam.setSamplingFunc(getSamplingFunction());
int i, j;
#pragma omp parallel private(i, j)
{
// Construct a ray for each pixel.
#pragma omp for schedule(dynamic, 4)
for (i = 0; i < cam.height(); ++i) {
for (j = 0; j < cam.width(); ++j) {
cam.computePixel(i, j);
}
}
}
}
この質問を読んだとき、私は自分の答えを見つけたと思いました。スレッド間で乱数を生成するための状態を保持するために、それ自体への呼び出しを同期するgclib rand()の実装について説明します。モンテカルロサンプリングにrand()を多用しているので、それが問題だと思いました。randへの呼び出しを取り除き、それらを単一の値に置き換えましたが、複数のスレッドを使用するとまだ時間がかかります。編集:おっと、私はこれを正しくテストしなかったことがわかりました、それはランダムな値でした!
これらが邪魔にならないので、への各呼び出しで行われていることの概要について説明しますcomputePixel
。解決策が見つかることを願っています。
私のレイトレーサーには、基本的にすべてのオブジェクトを含むシーンツリーがあります。このツリーは、オブジェクトの交差がテストされるときに頻繁にトラバースcomputePixel
されますが、このツリーまたはオブジェクトへの書き込みは行われません。computePixel
基本的に、シーンを何度も読み取り、オブジェクトのメソッド(すべてconstメソッド)を呼び出し、最後に単一の値を独自のピクセル配列に書き込みます。これは、複数のスレッドが同じメンバー変数に書き込もうとする場所を私が知っている唯一の部分です。2つのスレッドがピクセル配列の同じセルに書き込むことができないため、どこにも同期はありません。
誰かが何らかの論争があるかもしれない場所を提案できますか?試してみませんか?
前もって感謝します。
編集: 申し訳ありませんが、私のシステムに関する詳細情報を提供しないのは愚かでした。
- コンパイラgcc4.6(-O2最適化あり)
- Ubuntu Linux 11.10
- OpenMP 3
- Intel i3-2310Mクアッドコア2.1Ghz(現時点では私のラップトップ上)
計算ピクセルのコード:
class Camera {
// constructors destructors
private:
// this is the array that is being written to, but not read from.
Colour* _sensor; // allocated using new at construction.
}
void Camera::computePixel(int i, int j) const {
Colour col;
// simple code to construct appropriate ray for the pixel
Ray3D ray(/* params */);
col += _sceneSamplingFunc(ray); // calls a const method that traverses scene.
_sensor[i*_scrWidth+j] += col;
}
提案から、速度低下を引き起こすのはツリートラバーサルである可能性があります。他のいくつかの側面:サンプリング関数が呼び出されると、かなり多くの再帰が発生します(光線の再帰的なバウンス)-これはこれらの問題を引き起こす可能性がありますか?