c++ - 考虑缓存一致性的高性能应用程序的 POD 数学结构类的 C++ 值传递与引用传递选择
问题描述
对于许多高性能应用程序,例如游戏引擎或金融软件,缓存一致性、内存布局和缓存未命中的考虑对于保持流畅的性能至关重要。随着 C++ 标准的发展,特别是随着Move Semantics和C++14的引入,对于基于 POD 的数学类,何时绘制按值传递与按引用传递的界限变得不太清楚。
考虑常见的POD Vector3 类:
class Vector3
{
public:
float32 x;
float32 y;
float32 z;
// Implementation Functions below (all non-virtual)...
}
这是游戏开发中最常用的数学结构。它是一个非虚拟的 12 字节大小的类,即使是 64 位,因为我们明确使用 IEEE float32,它每个浮点使用 4 个字节。我的问题如下 -在决定通过价值或参考通过 POD 数学类以用于高性能应用程序时,使用的一般最佳实践指南是什么?
回答这个问题时需要考虑的一些事项:
- 假设默认构造函数没有初始化任何值是安全的
- 可以安全地假设任何 POD 数学结构都没有使用超过一维的数组
- 显然大多数人按值传递 4-8 字节的 POD 常量,所以那里似乎没有太多争论
- 当这个 Vector 是类成员变量而不是堆栈上的局部变量时会发生什么?如果使用引用传递,那么它将使用类上变量的内存地址与堆栈上本地的内存地址。这个用例重要吗?使用 PBR 的这种差异会导致更多的缓存未命中吗?
- 使用或不使用 SIMD 的情况如何?
- 移动语义编译器优化呢?我注意到,当切换到 C++14 时,当链函数调用按值传递相同的向量时,编译器通常会使用移动语义,尤其是当它是 const 时。我通过阅读装配故障观察到这一点
- 当对这些数学结构使用按值传递和按引用传递时,const对编译器优化有很大影响吗?见上一点
鉴于上述情况,对于现代 C++ 编译器(C++14 及更高版本)何时使用按值传递与按引用传递来最小化缓存未命中并提高缓存一致性,有什么好的指导方针?什么时候有人会说这个 POD 数学结构太大而无法按值传递,例如 4v4 仿射变换矩阵,假设使用 float32,其大小为 64 字节。在做出此决定时,在堆栈上声明的 Vector,或者更确切地说是任何小的 POD 数学结构与作为成员变量引用是否重要?
我希望有人可以提供一些分析和见解,以便为上述情况建立一个良好的现代最佳实践指南。我相信随着 C++ 标准的发展,何时将 PBV 与 PBR 用于 POD 类的界限变得更加模糊,尤其是在最大限度地减少缓存未命中方面。
解决方案
我看到问题标题是关于按值传递与按引用传递的选择,尽管听起来您更广泛地追求的是有效传递 3D 矢量和其他常见 POD 的最佳实践。传递数据是基本的,并且与编程范式交织在一起,因此对于最佳方式没有达成共识。除了性能之外,还有一些需要权衡的因素,例如代码可读性、灵活性和可移植性,以决定在给定应用程序中采用哪种方法。
也就是说,近年来,“面向数据的设计”已成为面向对象编程的流行替代方案,尤其是在视频游戏开发中。基本思想是考虑程序需要处理的数据,以及如何在内存中组织所有数据以获得良好的缓存局部性和计算性能。在 CppCon 2014 上有一个很棒的讨论:Mike Acton 的“面向数据的设计和 C++”。
以您的 Vector3 示例为例,通常情况下,程序不仅有一个,而且有许多 3D 矢量,它们都以相同的方式处理,例如,都经历相同的几何变换。面向数据的设计表明,将向量连续放置在内存中并在批处理操作中将它们全部转换在一起是一个好主意。这改进了缓存并创造了利用 SIMD 指令的机会。您可以使用Eigen C++ 线性代数库来实现这个示例。向量可以使用Eigen::Matrix<float, 3, Eigen::Dynamic>
3xN 的形状来表示以存储 N 个向量,然后使用 Eigen 的 SIMD 加速操作进行操作。
推荐阅读
- android - RecyclerView notifyItemChanged 调用 onBindViewHolder 也在不可见的单元格上
- linux - 如果验证失败,如何再次询问用户输入并获取新值?
- android - URI 无法通过 android 访问,但可以通过 UWP 访问
- python - 如何仅使用 pycharm 检查代码功能分析 python 文件?
- python - Django Admin中的外键和其他字段聚合
- firebase - `Firebase` 功能日志中的错误消息:
- c# - 通过变量 UWP 更改按钮 IsEnable
- php - 如何调用内核终止事件
- javascript - 香草JS。从每个方法中的数组中删除对象?
- javascript - PhantomJS 的意外结果