【问题标题】:C++ Memory alignment - should we care? [duplicate]C++ 内存对齐 - 我们应该关心吗? [复制]
【发布时间】:2019-12-06 06:13:42
【问题描述】:

考虑使用具有以下类型对齐的 x64 位 Windows 操作系统:

据我所知,这样做是非常糟糕的:

struct X_chaotic
{
    bool flag1;
    double d1;
    bool flag2;
    double d2;
    bool flag3;
    double d3;
    //... and so on ...
};

根据C++ Alignment, Cache Line and Best PracticeData structure alignment, 写这个应该更好/更快,更紧凑:

struct X_alignOrder
{
    double d1;
    double d2;
    double d3;
    //... all other doubles ...
    bool flag1;
    bool flag2;
    bool flag3;
    //... all other bools ...
};

成员按照对齐大小的顺序声明,从最高对齐开始。

可以肯定地说按对齐大小对数据成员的声明进行排序是个好主意吗?你会说这是最佳实践吗?还是没有区别?

(我听说编译器不能重新排列定义的顺序,由于 C++ 标准,这甚至适用于类的访问说明符块中声明的所有数据成员)

因为我从未在 Scott Meyers 的书中或 Bjarne Stroustrup 的书中读到过这方面的内容,所以我想知道是否应该开始为我的日常工作通过对齐来重新排序数据声明。

【问题讨论】:

  • 如果您进行过搜索,您可以在这里阅读到它。

标签: c++ memory-alignment


【解决方案1】:

这比看起来要复杂。

通过根据对齐需要对成员进行排序,您将节省一些填充字节并且总大小会更小。这可能对您来说很重要,如果内存紧张或者这意味着该类型可以放入单个缓存行而不是两个或三个缓存行。

另一方面;如果您经常访问过去靠得很近的成员,那么它们之前经常会被 CPU 预取器一起拉入缓存,但现在在重组类之后不会。那么您可能会节省内存,但会牺牲运行时性能。

这里的性能也可能因不同的 CPU 和不同的编译器/编译器选项而有很大差异。

您需要在实际环境中运行一些基准测试,以了解最适合您的。

另外请记住,重新洗牌你的成员变量会改变初始化的顺序,如果成员相互依赖,这可能很重要(foo 初始化 bar,所以 foo 需要首先初始化,等等)。

【讨论】:

  • “这比看起来更复杂”,此外,这也会改变成员的初始化顺序(带有初始化列表),这可能会改变如果成员之间存在一些依赖关系,则构造函数。
  • @Jarod42 好点。加了一句来说明这一点。
  • @Jarod42 那么,构造函数初始值设定项列表中项目的顺序无关紧要吗?实际上是声明成员的顺序?
  • @JerryJeremiah:没错,当两者不匹配时,您甚至可能会收到警告。
【解决方案2】:

是的。从理论上讲,如果您关心性能,那么数据结构的对齐很重要。这也是一种很好的编程习惯。

大多数时候,数据结构的对齐方式是根据“结构”的最宽成员设置的。通常,您的编译器会为您处理它。但是,在插入前导填充时,C++ 和 C 的行为可能有所不同。

您可以使用offsetof macro 来评估size_t 中给定struct 成员的距离。不过,这是 ANSI C。

#include <stdio.h>
#include <stddef.h>

typedef struct Test_t {
    char *p;
    char c;
    int i;
    long l;
} Test;

int main(){
    printf("offsetof(Test,p) = %zu\n", offsetof(Test,p));
    printf("offsetof(Test,c) = %zu\n", offsetof(Test,c));
    printf("offsetof(Test,i) = %zu\n", offsetof(Test,i));
    printf("offsetof(Test,l) = %zu\n", offsetof(Test,l));
    return 0;
}

这将打印出来

offsetof(Test,p) = 0
offsetof(Test,c) = 8
offsetof(Test,i) = 12
offsetof(Test,l) = 16

【讨论】:

  • 那么,offsetof(Test,i) = 12 表明c 之后有 3 个字节的填充,对吧?
猜你喜欢
  • 2011-02-25
  • 2016-03-08
  • 1970-01-01
  • 2012-08-11
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
相关资源
最近更新 更多