缓存友好顶点定义
Cache Friendly Vertex Definition
我正在编写一个 opengl 应用程序,对于顶点、法线和颜色,我使用如下单独的缓冲区:
GLuint vertex_buffer, normal_buffer, color_buffer;
我的主管告诉我,如果我定义一个像这样的结构:
struct vertex {
glm::vec3 pos;
glm::vec3 normal;
glm::vec3 color;
};
GLuint vertex_buffer;
然后定义这些顶点的缓冲区,我的应用程序会变得非常快,因为当位置被缓存时,法线和颜色将在缓存行中。
我认为定义这样的结构对性能没有太大影响,因为像结构一样定义顶点会导致缓存行中的顶点减少,而将它们定义为单独的缓冲区时,将导致有 3 个不同的缓存缓存中位置、法线和颜色的行。所以,什么都没有改变。真的吗?
取决于 GPU 架构。
大多数 GPU 都有多个缓存行(一些用于制服,另一些用于顶点属性,另一些用于纹理采样)
此外,当顶点着色器即将完成时,GPU 可以将下一组属性预取到缓存中。这样当顶点着色器完成时,下一个属性就在那里准备加载到寄存器中。
tl;dr 不要理会这些 "rule of thumbs" 除非你真的分析它或知道 GPU 的实际架构。
告诉你的主管 "premature optimization is the root of all evil" – Donald E. Knuth。不过别忘了下一句"but that doesn't mean we shouldn't optimize hot spots".
那么你真的描述了差异吗?
无论如何,顶点数据的布局对于现代 GPU 上的缓存效率并不重要。它曾经在旧的 GPU 上(大约 2000 年),这就是为什么有交织顶点数据的功能。但现在这几乎不是问题了。
这与现代 GPU 访问内存的方式有关,事实上现代 GPU 的缓存行不是按内存地址索引,而是按访问模式(即着色器中的第一个不同的内存访问获得第一个缓存行) ,第二个是第二个缓存行,依此类推)。
首先,为不同的顶点属性使用单独的缓冲区可能不是一个好的技术。
这里非常重要的因素是 GPU 架构。大多数(尤其是现代的)GPU 有多个缓存行(输入汇编器阶段的数据、制服、纹理),但是从多个 VBO 获取输入属性无论如何都是低效的(总是配置文件!)。以交错格式定义它们有助于提高性能:
如果你使用这样的结构,这就是你会得到的。
然而,这并不总是正确的(再一次,总是配置文件!)- 尽管交错数据对 GPU 更友好,但它需要正确对齐并且可能占用更多 space 内存。
但是,一般来说:
Interleaved data formats:
Cause less GPU cache pressure, because the vertex coordinate and attributes of a single vertex aren't scattered all over in memory.
They fit consecutively into few cache lines, whereas scattered
attributes could cause more cache updates and therefore evictions. The
worst case scenario could be one (attribute) element per cache line at
a time because of distant memory locations, while vertices get pulled
in a non-deterministic/non-contiguous manner, where possibly no
prediction and prefetching kicks in. GPUs are very similar to CPUs in
this matter.
Are also very useful for various external formats, which satisfy the deprecated interleaved formats, where datasets of compatible data
sources can be read straight into mapped GPU memory. I ended up
re-implementing these interleaved formats with the current API for
exactly those reasons.
Should be layouted alignment friendly just like simple arrays. Mixing various data types with different size/alignment requirements
may need padding to be GPU and CPU friendly. This is the only downside
I know of, appart from the more difficult implementation.
Do not prevent you from pointing to single attrib arrays in them for sharing.
Source
进一步阅读:
我正在编写一个 opengl 应用程序,对于顶点、法线和颜色,我使用如下单独的缓冲区:
GLuint vertex_buffer, normal_buffer, color_buffer;
我的主管告诉我,如果我定义一个像这样的结构:
struct vertex {
glm::vec3 pos;
glm::vec3 normal;
glm::vec3 color;
};
GLuint vertex_buffer;
然后定义这些顶点的缓冲区,我的应用程序会变得非常快,因为当位置被缓存时,法线和颜色将在缓存行中。
我认为定义这样的结构对性能没有太大影响,因为像结构一样定义顶点会导致缓存行中的顶点减少,而将它们定义为单独的缓冲区时,将导致有 3 个不同的缓存缓存中位置、法线和颜色的行。所以,什么都没有改变。真的吗?
取决于 GPU 架构。
大多数 GPU 都有多个缓存行(一些用于制服,另一些用于顶点属性,另一些用于纹理采样)
此外,当顶点着色器即将完成时,GPU 可以将下一组属性预取到缓存中。这样当顶点着色器完成时,下一个属性就在那里准备加载到寄存器中。
tl;dr 不要理会这些 "rule of thumbs" 除非你真的分析它或知道 GPU 的实际架构。
告诉你的主管 "premature optimization is the root of all evil" – Donald E. Knuth。不过别忘了下一句"but that doesn't mean we shouldn't optimize hot spots".
那么你真的描述了差异吗?
无论如何,顶点数据的布局对于现代 GPU 上的缓存效率并不重要。它曾经在旧的 GPU 上(大约 2000 年),这就是为什么有交织顶点数据的功能。但现在这几乎不是问题了。
这与现代 GPU 访问内存的方式有关,事实上现代 GPU 的缓存行不是按内存地址索引,而是按访问模式(即着色器中的第一个不同的内存访问获得第一个缓存行) ,第二个是第二个缓存行,依此类推)。
首先,为不同的顶点属性使用单独的缓冲区可能不是一个好的技术。
这里非常重要的因素是 GPU 架构。大多数(尤其是现代的)GPU 有多个缓存行(输入汇编器阶段的数据、制服、纹理),但是从多个 VBO 获取输入属性无论如何都是低效的(总是配置文件!)。以交错格式定义它们有助于提高性能:
如果你使用这样的结构,这就是你会得到的。
然而,这并不总是正确的(再一次,总是配置文件!)- 尽管交错数据对 GPU 更友好,但它需要正确对齐并且可能占用更多 space 内存。
但是,一般来说:
Interleaved data formats:
Cause less GPU cache pressure, because the vertex coordinate and attributes of a single vertex aren't scattered all over in memory. They fit consecutively into few cache lines, whereas scattered attributes could cause more cache updates and therefore evictions. The worst case scenario could be one (attribute) element per cache line at a time because of distant memory locations, while vertices get pulled in a non-deterministic/non-contiguous manner, where possibly no prediction and prefetching kicks in. GPUs are very similar to CPUs in this matter.
Are also very useful for various external formats, which satisfy the deprecated interleaved formats, where datasets of compatible data sources can be read straight into mapped GPU memory. I ended up re-implementing these interleaved formats with the current API for exactly those reasons.
Should be layouted alignment friendly just like simple arrays. Mixing various data types with different size/alignment requirements may need padding to be GPU and CPU friendly. This is the only downside I know of, appart from the more difficult implementation.
Do not prevent you from pointing to single attrib arrays in them for sharing.
Source
进一步阅读: