面向数据的设计;如何在 C++ 中优化数据结构以提高性能?

Data Orientated Design; how do I optimize a data structure in c++ for performance?

本文关键字:优化 C++ 数据结构 高性能 数据      更新时间:2023-10-16

我希望有一个不同数量的n个对象的类,这些对象很容易作为一个组进行迭代,每个对象成员都有一个影响类方法的单独修改变量的大列表(20+(。 在我开始学习 OOP 之前,我只会创建一个 2D 数组并将变量值加载到对应于每个对象的每行中,然后根据需要追加/删除行。 这仍然是一个好的解决方案吗? 有没有更好的解决方案?

同样,在这种情况下,我更感兴趣的是提高处理器性能,而不是保留抽象和模块化等。 在这方面,我对数据容器最终读入 L1 缓存的方式以及如何确保我不会引起页面效率低下或缓存未命中感到非常困惑。 例如,如果我有一个 128 kb 的缓存,我认为整个容器应该适合这个缓存才能高效,对吗?

根据 Agner Fog 的优化手册,C++ 标准模板库效率相当低,因为它广泛使用了动态内存分配。但是,大于必要大小的固定大小数组(例如,因为在编译时不知道所需的大小(也可能不利于性能,因为较大的大小意味着它不会轻易放入缓存中。在这种情况下,STL 的动态内存分配可以更好地执行。

通常,最好将数据存储在连续内存中。为此,您可以使用固定大小的数组或std::vector。但是,在使用std::vector之前,出于性能原因,您应该调用std::vector::reserve(),以便不必过于频繁地重新分配内存。如果过于频繁地重新分配,堆可能会变得碎片化,这也不利于缓存性能。

理想情况下,您正在处理的数据将完全适合 1 级数据缓存(在现代台式机处理器上约为 32 KB(。但是,即使它不适合,2 级缓存也要大得多(约 512 KB(,而 3 级缓存是几兆字节。更高级别的缓存仍然比从主内存读取要快得多。

最好是内存访问模式是可预测的,以便硬件预取程序可以最好地完成其工作。对于硬件预取程序来说,顺序内存访问最容易预测。

如果多次访问相同的数据,并且数据足够小,可以保存在缓存中,则 CPU 缓存效果最佳。但是,即使数据仅使用一次,CPU 缓存仍可以通过使用预取来加快内存访问速度。

如果出现以下情况,则会发生缓存未命中

  1. 数据是首次访问,硬件预取程序无法及时预测和预取所需的内存地址,或者
  2. 数据
  3. 不再缓存,因为缓存必须为其他数据腾出空间,因为数据太大而无法放入缓存。

除了硬件预取程序尝试提前预测所需的内存地址(这是自动的(之外,程序员还可以明确发出软件预取。但是,从我所读到的内容来看,除非在非常特殊的情况下,否则很难从这样做中获得显着的性能提升。