关于为什么新代码会减慢光线跟踪器的速度的任何想法

Any ideas as to why the new code slows down the ray-tracer?

本文关键字:光线跟踪 速度 任何想 新代码 为什么 代码      更新时间:2023-10-16

我正在进行光线跟踪器,并实现了透视校正,使用x和y轴上的当前像素值计算要发射的光线的位置,以计算当前光线方向。这里的代码片段:

float fov = 60; 
float invWidth = 1/float(image.getWidth());
float invHeight = 1/float(image.getHeight());
float angle = (fov * M_PI * 0.5/180 );
float aspectratio = image.getWidth()/float(image.getHeight());
point camera = scene.getCamera();
for (int y=0;  y<image.getHeight(); y++) { 
  for (int x=0; x<image.getWidth(); x++) {
    ......
    ......
    float xx = (((x*invWidth) *2)-1) * angle * aspectratio; 
    float yy = (((y*invHeight)*2)-1) * angle;
    Ray viewRay = { {camera.x, camera.y, camera.z}, {xx, yy, 1.0f}}; 

到目前为止,效果非常好。然而,我意识到xx和yy(像素的方向)的值不需要为每个像素计算,只需要几倍于图像的宽度和长度。所以我用这种方式重写了这些部分:

float fov = 60; 
float invWidth = 1/float(image.getWidth());
float invHeight = 1/float(image.getHeight());
float angle = (fov * M_PI * 0.5/180 );
float aspectratio = image.getWidth()/float(image.getHeight());
float rays_x [image.getWidth()], rays_y [image.getHeight()];
for (int y=0; y<image.getHeight(); y++)
    rays_y [y] = (((y*invHeight)*2)-1) * angle;
for (int x=0; x<image.getWidth(); x++)
    rays_x [x] = (((x*invWidth) *2)-1) * angle * aspectratio;
point camera = scene.getCamera();
for (int y=0;  y<image.getHeight(); y++) { 
  float yy = rays_y[y];
  for (int x=0; x<image.getWidth(); x++) {
    ......
    ......
    Ray viewRay = { {camera.x, camera.y, camera.z}, {rays_x[x], yy, 1.0f}};

我基本上预先计算了光线的方向,并将它们存储到阵列中。我预计性能会有一些小的改进,在悲观的情况下可能不会,但我从未想过会变得最糟糕。以前我渲染一个场景需要1.67秒,现在需要1.74秒!这并不是一个巨大的下降,但令人惊讶的是,我本以为现在会做更少的工作。我禁用了编译器优化(-O3和-fast math),并使用这两种方法进行了测试。之前它在9.03和9.05之间嘟嘟作响,现在在9.06和9.15之间

那么我该如何调查此事呢?我唯一想到的是,由于每次循环迭代都访问rays_x[x],每1024次迭代访问rays_y[y],所以缓存命中率更低,尽管我永远不会怀疑这一点,因为总共只有1024*4=4096+(768*4)=7168个字节。任何想法都将不胜感激。

编译器将意识到这一点:

float yy = (((y*invHeight)*2)-1) * angle;

是常量数据,每个循环只需要计算一次。

因此,预先计算的yy是对性能的浪费。

不过,预先计算的xx可能会有所帮助,但如果表达式保持许多恒定数据(即invWidth*2angle*aspectratio),则性能可能不会提高,甚至可能因缓存未命中而变得更差。

float xx = (((x*invWidth) *2)-1) * angle * aspectratio; 

预先计算方向将加速跟踪器。但是,首先创建查找表显然会产生开销。在代码中,您正在堆栈上创建表,并重新计算每一帧的方向。这将稍微慢一点,因为您必须从以前没有读取的数组中读取,并且由于内存分配开销。相反,我建议您在堆上创建查找数组(作为方法外的指针),并只预计算一次方向。方向取决于帧之间不变的值,因此不需要计算每帧的方向。

类似这样的东西:

float *rays_x, *rays_y;
void compute_directions()
{
    rays_x = new float[image.getWidth()];
    rays_y = new float[image.getHeight()];
    for (int y=0; y<image.getHeight(); y++)
        rays_y[y] = (((y*invHeight)*2)-1) * angle;
    for (int x=0; x<image.getWidth(); x++)
        rays_x[x] = (((x*invWidth) *2)-1) * angle * aspectratio;
}
void render()
{
    float fov = 60; 
    float invWidth = 1/float(image.getWidth());
    float invHeight = 1/float(image.getHeight());
    float angle = (fov * M_PI * 0.5/180 );
    float aspectratio = image.getWidth()/float(image.getHeight());
    point camera = scene.getCamera();
    for (int y=0;  y<image.getHeight(); y++) { 
    float yy = rays_y[y];
    for (int x=0; x<image.getWidth(); x++) {
        ......
        ......

很明显,你必须将角度和方位移动到其他地方,这样你就可以在compute_directions中访问它们。此外,如果您不再需要指针,请记住使用delete[]删除指针,以防止内存泄漏。

根据您的描述判断,您似乎通过预计算一些似乎计算得很快的值(将计算转移到一些可能不会带来任何性能改进的内存查找上,这也是一种预感!)来优化。

优化的一些基本规则:

  • 在尝试优化任何内容之前:配置文件
  • 在优化任何东西之后:配置文件

在知道程序实际花费的时间之前,您不能指望通过优化获得任何性能增益。

在Linux上,您可以使用GC-pg开关和gprof。您还可以使用perf和valgrind(例如callgrind来了解对特定函数的调用次数)。

还可以查看perfwiki。