c++ - 考虑到缓存一致性的高性能应用程序的POD数学结构类的C++选择按值传递还是按引用传递

标签 c++ performance memory-management move-semantics data-oriented-design

对于许多高性能应用程序(例如游戏引擎或财务软件),对缓存一致性,内存布局和缓存未命中的考虑对于保持流畅的性能至关重要。随着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,每个float使用4个字节。我的问题如下-在为高性能应用程序决定按值或按引用传递POD数学类时,要使用的一般最佳实践准则是什么?
回答此问题时需要考虑的一些事项:
  • 可以安全地假定默认构造函数未初始化任何值
  • 可以安全地假设任何POD数学结构均不使用超过1D的数组
  • 显然,大多数人都按值传递4-8字节的POD常量,因此
  • 似乎没有太多争议
  • 如果此Vector是堆栈中的类成员变量与局部变量,会发生什么?如果使用按引用传递,则它将使用该类上变量的内存地址与堆栈上本地变量的内存地址。这个用例重要吗?使用PBR的这种差异是否会导致更多的缓存未命中?
  • 使用或不使用SIMD的情况如何?
  • 移动语义编译器优化怎么样?我已经注意到,当切换到C++ 14时,当链函数调用通过值传递相同的 vector 时,尤其是在const时,编译器将经常使用move语义。我通过仔细阅读程序分解
  • 来观察到这一点
  • 在这些数学结构中使用按值传递和按引用传递时, const 是否会对编译器优化产生很大影响?见以上几点

  • 鉴于上述情况,什么时候在现代C++编译器(C++ 14及更高版本)中使用按值传递与按引用传递以减少高速缓存未命中并提高高速缓存一致性的良好指南是什么?有人会在什么时候说此POD数学结构太大而无法按值传递,例如4v4仿射变换矩阵,假设使用float32,它的大小为64字节。在做出此决定时,在堆栈上声明的Vector或任何小的POD数学结构与被引用为成员变量是否重要?
    我希望有人可以提供一些分析和见解,以针对上述情况建立最佳的现代最佳实践指南。我相信随着C++标准的发展,关于何时将PBV和PBR用于POD类的界限已经变得越来越模糊,尤其是在最大程度地减少高速缓存未命中方面。

    最佳答案

    我看到问题的标题是按值传递还是按引用传递的选择,尽管听起来更广泛地讲,这是有效传递3D vector 和其他常见POD的最佳实践。传递数据是基本的,并且与编程范式交织在一起,因此,关于最佳处理方式尚未达成共识。除了性能以外,还需要权衡一些因素,例如代码的可读性,灵活性和可移植性,以决定在给定应用程序中哪种方法更受青睐。
    也就是说,近年来,"data-oriented design"已成为面向对象编程的流行替代方法,尤其是在视频游戏开发中。基本思想是根据程序需要处理的数据来考虑程序,以及如何在内存中组织所有这些数据以获得良好的缓存局部性和计算性能。在CppCon 2014上有一个很好的话题:"Data-Oriented Design and C++" by Mike Acton
    例如,以您的Vector3示例为例,通常情况下,一个程序不仅具有一个3D vector ,而且许多3D vector 都以相同的方式处理,例如,它们都经历相同的几何变换。面向数据的设计表明,最好将 vector 连续地放置在内存中,并在批处理操作中将它们全部转换在一起。这改善了缓存,并创造了利用SIMD指令的机会。您可以使用Eigen C++ linear algebra library实现此示例。可以使用形状为3xN的Eigen::Matrix<float, 3, Eigen::Dynamic>表示 vector 来存储N个 vector ,然后使用Eigen的SIMD加速操作对其进行操作。

    关于c++ - 考虑到缓存一致性的高性能应用程序的POD数学结构类的C++选择按值传递还是按引用传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63785589/

    相关文章:

    c++ - 自定义类型 Qlist 和范围

    c++ - C++ 宏中的随机数

    mysql - 如何改进具有多个 JOINS 的 MYSQL 查询

    c - 使用 free() 释放内存时遇到问题

    c++ - Win32 - 使用 GDI 对象的正确方法是什么?

    c++ - 没有虚拟析构函数可能会发生内存泄漏?

    c - 如何测量一小段 C/汇编代码的速度?

    performance - 大型 MatLab 图形的渲染速度很慢

    optimization - 如何在优化方面做得更好?

    matlab - 如果 RAM 使用量超过指定数量,立即停止 MATLAB。有办法吗?