我正在编写一个小型渲染器(基于光栅化算法)。这是我为测试不同技术而做的个人项目。我正在测量渲染一堆三角形所花费的时间,而在这样做时我发现了一些奇怪的东西。如果给定像素与2D三角形重叠并通过一些其他测试(它在缓冲区中写入该三角形的颜色),则程序执行的操作是写入图像缓冲区(Vec3ui的1D数组)。
Vec3<unsigned char> *fb = new Vec3<unsigned char>[w * h];
...
void rasterize(
...,
Vec3<unsigned char> *&fb,
float *&zbuffer)
{
Vec3<unsigned char> randcol(drand48() * 255, drand48() * 255, drand48() * 255);
...
uint32_t x, y;
// loop over bounding box of triangle
// check if given pixel is in triangle
for (y = ymin, p.y = ymin; y <= ymax; ++y, ++p.y)
{
for (x = xmin, p.x = xmin; x <= xmax; ++x, ++p.x)
{
if (pixelOverTriangle(...) {
fb[y * w + x] = randcol;
}
}
}
}
在我测量统计数据时,我认为实际上需要花费最长时间才能渲染三角形,进行所有测试等等。当我运行具有给定数量的三角形的程序时,我会得到以下渲染时间:
74 ms
但是当我注释掉我写入图像缓冲区的行时,我得到了:
5 ms
所以要明确我这样做:
if (pixelOverTriangle(...) {
// fb[y * w + x] = randcol;
}
事实上,超过90%的时间用于写入图像缓冲区!
我不得不说我尝试优化了如何计算用于访问数组中元素的索引,但这不是时间。时间实际上是将变量向右复制到缓冲区中(所以无论如何都是这样)。
我对这些数字感到非常惊讶。
所以我有几个问题:
答案 0 :(得分:2)
内存读/写比C ++看起来要多得多。通常,您的处理器会缓存内存块以便快速访问;这大大提高了连续内存中数据的性能:例如数组,结构和堆栈。但是,在尝试访问尚未缓存的内存(缓存未命中)时,处理器必须缓存新的内存块,这需要更长的时间(几分钟甚至几小时缩放到第二个长周期)。通过访问长内存块的任意段 - 就像您的图像一样 - 您几乎可以保证连续缓存未命中。
更糟糕的是,计算机内存(RAM)实际上位于虚拟页面上,这些虚拟页面一直在物理内存中交换。如果你的图像大到可以跨越多个内存页面(通常每个大约4kb),那么你的操作系统实际上是从二级存储(你的硬盘驱动器)加载和卸载数据,你可以想象它比从内存直接读取的时间长得多
我从另一个有关缓存性能的stackoverflow问题中找到an article,它可能比我更好地回答您的问题。实际上,了解内存读/写实际正在做什么以及如何能够显着影响性能是非常重要的。
答案 1 :(得分:1)
您可能需要查看的答案......
编译器可能会注意到您的代码什么都不做并删除它。查看函数的反汇编,看看它是否实际进行了任何计算。