写入内存缓冲区时的性能损失 (C++)
Performance Loss when Writing to Memory Buffer (C++)
我正在写一个小型渲染器(基于光栅化算法)。这是我正在做的一个个人项目,用于测试不同的技术。我正在测量渲染一堆三角形所花费的时间,在这样做时我注意到了一些奇怪的事情。如果给定像素与 2D 三角形重叠并通过一些其他测试(它在缓冲区中写入该三角形的颜色),程序所做的是写入图像缓冲区(Vec3ui 的一维数组)。
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% 以上的时间都花在了写入图像缓冲区上!
我不得不说我尝试优化用于访问数组中元素的索引的计算方式,但这不是时间流逝的地方。时间进入了实际将变量复制到缓冲区的权利(无论如何看起来)。
我对这些数字感到非常惊讶。
所以我有几个问题:
- 是否符合预期?
- 我是不是做错了什么?
- 我可以让它变得更好吗?我可以使用什么技术来优化它?
您必须检查的可能答案...
编译器可能会注意到您的代码没有执行任何操作并将其删除。查看函数的反汇编,看看它是否真的在进行任何计算。
内存读/写比 C++ 看起来要多得多。通常情况下,您的处理器会缓存内存块以便快速访问;这极大地提高了连续内存中数据的性能:例如数组、结构和堆栈。但是,在尝试访问尚未缓存的内存(缓存未命中)时,处理器必须缓存一个新的内存块,这需要更长的时间(几分钟甚至几小时扩展到秒长周期)。通过访问长内存块的任意段——比如你的图像——你实际上保证了连续的缓存未命中。
更糟糕的是,计算机内存 (RAM) 实际上位于虚拟页面上,这些虚拟页面一直在交换进出物理内存。如果你的图像足够大,可以跨越多个内存页(通常每个大约 4kb),那么你的操作系统实际上是从辅助存储(你的硬盘驱动器)加载和卸载数据,你可以想象这比直接从内存读取花费的时间要长得多.
我从另一个关于缓存性能的 Whosebug 问题中发现 an article,它可能比我更能回答您的问题。真的,重要的是要了解内存 read/write 实际在做什么,以及它如何显着影响性能。
我正在写一个小型渲染器(基于光栅化算法)。这是我正在做的一个个人项目,用于测试不同的技术。我正在测量渲染一堆三角形所花费的时间,在这样做时我注意到了一些奇怪的事情。如果给定像素与 2D 三角形重叠并通过一些其他测试(它在缓冲区中写入该三角形的颜色),程序所做的是写入图像缓冲区(Vec3ui 的一维数组)。
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% 以上的时间都花在了写入图像缓冲区上!
我不得不说我尝试优化用于访问数组中元素的索引的计算方式,但这不是时间流逝的地方。时间进入了实际将变量复制到缓冲区的权利(无论如何看起来)。
我对这些数字感到非常惊讶。
所以我有几个问题:
- 是否符合预期?
- 我是不是做错了什么?
- 我可以让它变得更好吗?我可以使用什么技术来优化它?
您必须检查的可能答案...
编译器可能会注意到您的代码没有执行任何操作并将其删除。查看函数的反汇编,看看它是否真的在进行任何计算。
内存读/写比 C++ 看起来要多得多。通常情况下,您的处理器会缓存内存块以便快速访问;这极大地提高了连续内存中数据的性能:例如数组、结构和堆栈。但是,在尝试访问尚未缓存的内存(缓存未命中)时,处理器必须缓存一个新的内存块,这需要更长的时间(几分钟甚至几小时扩展到秒长周期)。通过访问长内存块的任意段——比如你的图像——你实际上保证了连续的缓存未命中。
更糟糕的是,计算机内存 (RAM) 实际上位于虚拟页面上,这些虚拟页面一直在交换进出物理内存。如果你的图像足够大,可以跨越多个内存页(通常每个大约 4kb),那么你的操作系统实际上是从辅助存储(你的硬盘驱动器)加载和卸载数据,你可以想象这比直接从内存读取花费的时间要长得多.
我从另一个关于缓存性能的 Whosebug 问题中发现 an article,它可能比我更能回答您的问题。真的,重要的是要了解内存 read/write 实际在做什么,以及它如何显着影响性能。