过去一周我一直在编写一个光线追踪器,并且已经达到了足够多线程的程度。我尝试过使用 OpenMP 来并行化它,但是使用更多线程运行它实际上比使用一个线程运行它要慢。
阅读其他类似的问题,尤其是有关 OpenMP 的问题,一个建议是 gcc 可以更好地优化串行代码。但是运行下面的编译代码export OMP_NUM_THREADS=1
速度是原来的两倍export OMP_NUM_THREADS=4
。 IE。两次运行时编译的代码相同。
运行程序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
用户时间比实际时间少很多,这在使用多核时是不常见的 -user应该大于real因为多个核心同时运行。
我使用 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 方法),并在最后将单个值写入其自己的像素数组。这是我所知道的唯一部分,其中多个线程将尝试写入同一成员变量。任何地方都没有同步,因为没有两个线程可以写入像素阵列中的同一单元。
谁能建议可能发生某种争论的地方?值得尝试的事情?
先感谢您。
EDIT:抱歉,我很愚蠢,没有提供有关我的系统的更多信息。
- 编译器 gcc 4.6(带 -O2 优化)
- 乌班图 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;
}
从建议来看,可能是树遍历导致速度减慢。其他一些方面:一旦调用采样函数,就会涉及相当多的递归(光线的递归弹跳)——这会导致这些问题吗?