2

我的问题是为什么 Visual Studio 2012 编译器不自动重新排序结构成员以获得最佳内存利用率?编译器似乎完全按照它们在结构定义中声明的顺序存储成员,并根据成员对齐所需的一些空填充。在可能的情况下,重新排序似乎是比填充更可取的对齐成员的方式。是否有理由必须按声明顺序将其存储在内存中?

相关细节如下;

我有一个结构,它代表一个大数组中的单个元素。该元素有许多成员,一些是 32 位的,一些是 64 位的。我已调整默认结构成员对齐以获得最佳性能。

我在调试模式下探索内存,发现有很大一部分内存被浪费了。我将问题跟踪到 stuct 成员如何在内存中对齐。我知道 32 位成员必须在 DWORD 边界上对齐以获得最佳性能,显然 64 位成员必须在 QWORD 边界上对齐(我原以为 DWORD 边界就足够了)

我可以通过更改在结构定义中列出成员的顺序来解决问题。我确保尽可能连续放置 2 个 32 位成员,以便在 QWORD 边界上启动下一个 64 位成员不需要填充。

4

4 回答 4

3

这是 C++ 标准,编译器无法修改字段的顺序,可能是因为程序员可能希望通过指向第一个字段的指针来访问字段。如果您需要自己重新排序,请查看这篇文章

第 9.2.13 节:

分配具有相同访问控制(第 11 条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。未指定具有不同访问控制的非静态数据成员的分配顺序(第 11 条)。实现对齐要求可能会导致两个相邻的成员不会被立即分配;管理虚拟功能 (10.3) 和虚拟基类 (10.1) 的空间要求也是如此。

于 2015-06-02T14:52:28.620 回答
1

标准布局结构或类中的数据必须做出一定的布局保证。除此之外,如果有另一个标准布局结构或类是第一个的前缀,您必须能够将一个结构重新解释为另一个,并且公共前缀必须一致。

这基本上强制标准布局结构的内存顺序与您声明它们的顺序相同。

这类似于 C 在结构布局方面的要求,如此处所述

现在,在 C++ 中,为非标准布局结构提供了一些自由度。

[expr.rel]/3 分点 3:

如果两个指针指向同一个对象的不同非静态数据成员,或者这些成员的子对象,递归地,如果两个成员具有相同的访问控制(第 11 条)并且提供了它们的阶级不是工会。

必须在公共/私有/受保护的访问控制域内维护元素的顺序。元素之间的空间几乎可以以任意方式添加。

这意味着您可以知道&this->x大于或小于&this->y,某些程序员可能会使用它。

在 as-if 规则下,如果没有人获取此类数据的地址,编译器可以重新排序它们。这在通常的编译模型中很难证明。

根据我的经验,MSVC 中元素之间的间距与普通旧数据结构中的间距相匹配,除非继承与虚拟玩游戏。布局兼容性(超出标准)对于稳定的 ABI 很重要,使用一个版本的编译器编译的代码更适合在另一个版本中工作。打破它是有代价的。

C++ 程序员可以根据需要对数据结构进行重新排序,Visual Studio 提供了#pragmas 来更改结构打包规则,因此如果您真的需要最后一点性能,您可以获得它。

如果需要,您甚至可以编写一个类似tuple- 的数据结构来保证最佳打包。(我不会依赖std::tuple,因为它没有包装保证)

于 2015-06-02T15:03:19.883 回答
0

没有#pragmas,内存不会被 C++ 打包并且不会重新排序,因为该语言保证布局与代码一致。想象一下将造成的破坏——将结构映射到文件(内存映射文件)或硬件将永远无法工作。

为了了解类或结构的布局,Visual C++ 提供了一个未记录的命令行参数/d1reportSingleClassLayout,该参数将为您绘制类/结构的内存布局的 ASCII 艺术图,包括所有成员、基本成员和 vtable。foo例如,如果您有一个名为 的类,请将其添加/d1reportSingleClassLayoutfoo到编译器命令行。

于 2015-06-02T14:54:19.210 回答
0

我怀疑这是重叠需求的交叉点。

  1. C 和 C++ 中相同 POD 结构的布局应该是二进制兼容的。(我不知道标准是否要求这样做,但大多数编译器供应商可能会优先考虑它,因为许多现有代码都依赖于它。)
  2. 除了默认可见性之外,C++ 中的结构和类实际上是相同的。
  3. 类的数据成员按照声明的顺序构造,并以相反的顺序销毁。

如果编译器要重新排序数据成员以获得更好的对齐和/或更紧密的打包,那是否应该改变构造/销毁顺序的顺序?不,这会破坏大量依赖 RAII 的代码。但是现在构造过程中的内存访问不那么有序了,这实际上可能是一种悲观化,这取决于缓存行为、结构的大小以及构造这些结构的频率。

您可能会争辩说这些问题不适用于 POD 结构,但要求 1 和 2 说 C++ 编译器必须以与类相同的方式布置 POD 结构(反之亦然)。

于 2015-06-02T17:59:07.170 回答