【问题标题】:Memory alignment : how to use alignof / alignas?内存对齐:如何使用 alignof / alignas?
【发布时间】:2013-06-10 02:14:16
【问题描述】:

我现在使用共享内存。

我听不懂alignofalignas

cppreference 不清楚:alignof 返回“对齐”但什么是“对齐”?为要对齐的下一个块添加的字节数?填充尺寸? Stack overflow/博客条目也不清楚。

有人能解释清楚alignofalignas吗?

【问题讨论】:

  • cppreference 试图成为参考而不是教程
  • @Cubbi:你也可以在 cplusplus.com 上查看,有争论哪个站点更好,对于某些主题 cplusplus 更好,对于其他 cppreference 更好,我发现两个站点在某些时候都不是够了
  • @DarioOO 我只是在回答为什么 cppreference 没有在alignof 页面上解释对齐的概念(它现在在进行中的工作object page 上解释了)。我看不出 cplusplus.com 有什么相关性。
  • 所有答案都在谈论性能,但有些平台的硬件无法加载未对齐的int

标签: c++ c++11 x86-64 memory-alignment


【解决方案1】:

对齐是对可以存储值的第一个字节的内存位置的限制。 (需要提高处理器的性能并允许使用某些指令,这些指令仅适用于具有特定对齐方式的数据,例如 SSE 需要对齐到 16 字节,而 AVX 需要对齐到 32 字节。)

16 对齐意味着只有 16 的倍数的内存地址是唯一有效的地址。

alignas

强制对齐到所需的字节数。您只能对齐 2 的幂:1、2、4、8、16、32、64、128、...

#include <cstdlib>
#include <iostream>

int main() {
    alignas(16) int a[4];
    alignas(1024) int b[4];
    printf("%p\n", a);
    printf("%p", b);
}

示例输出:

0xbfa493e0
0xbfa49000  // note how many more "zeros" now.
// binary equivalent
1011 1111 1010 0100 1001 0011 1110 0000
1011 1111 1010 0100 1001 0000 0000 0000 // every zero is just a extra power of 2

另一个关键字

alignof

很方便,你不能这样做

int a[4];
assert(a % 16 == 0); // check if alignment is to 16 bytes: WRONG compiler error

但你可以做到

assert(alignof(a) == 16);
assert(alignof(b) == 1024);

请注意,实际上这比简单的“%”(模数)运算更严格。事实上,我们知道对齐到 1024 字节的东西必然会对齐到 1、2、4、8 字节,但是

 assert(alignof(b) == 32); // fail.

所以更准确地说,“alignof”返回 2 的最大幂来对齐某物。

此外,alignof 也是提前了解基本数据类型的最小对齐要求的好方法(它可能会返回 1 表示字符,4 表示浮点数等)。

仍然合法:

alignas(alignof(float)) float SqDistance;

对齐为 16 的东西将被放置在下一个可用地址上,该地址是 16 的倍数(最后使用的地址可能有隐式填充)。

【讨论】:

  • sizeof不同,alignof只能应用于type-id
  • 是在编译时评估的alignof()(和对应的alignas()),所以没有运行时开销?
  • @Serthy 澄清alignof 一个编译时常量。 alignas 不是,并且必须由您的 new 实现(标准要求)或自定义标准 allocator 支持。
  • 很好的答案,但它需要处理structstatic 的结构成员。 alignas__attribute__((aligned)) 更挑剔,尤其是在像 Clang 这样的编译器下。
  • "对齐 16 意味着只有 16 的倍数的内存地址是唯一有效的地址。" 这是从哪里来的?根据 C++ Stadnard:对齐是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。.
【解决方案2】:

对齐不是填充(尽管有时会引入填充来满足对齐要求)。它是 C++ 类型的内在属性。用标准语言表达 (3.11[basic.align])

对象类型具有对齐要求(3.9.1、3.9.2),这些要求限制了可以分配该类型对象的地址。对齐是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。对象类型对该类型的每个对象都有对齐要求;可以使用对齐说明符 (7.6.2) 请求更严格的对齐。

【讨论】:

  • 非常有趣。你介意举一些例子吗? alignof(struct X) == sizeof(struct X) 吗?为什么不呢?
  • @Offirmo 没有,除非出于巧合:struct X { char a; char b} 在正常系统上的大小为 2,对齐要求为 1(它可以在任何地址分配,因为 char 可以在任何地址分配)跨度>
  • 对齐要求为 1 ????哦,我明白了:我认为对齐总是在“自然”的 32 位/64 位边界上,但显然不是。这解释了事情......所以在通常的机器上, alignof() 结果将始终最大为 4(32 位)或 8(64 位)我是对的吗?
  • @Offirmo "natural" alignof 将在 alignof(std::max_align_t) 达到最大值,在我的 linux 上是 16(无论是编译 -m32 还是 -m64),但您可以使用 @ 使其更严格987654325@
【解决方案3】:

每种类型都有对齐要求。通常,这样可以有效地访问该类型的变量,而不必导致 CPU 生成多个读/写访问以访问该数据类型的任何给定成员。此外,它还确保有效复制整个变量。 alignof 将返回给定类型的对齐要求。

alignas 用于强制对数据类型进行对齐(只要不低于alignof 所说的数据类型将返回的严格程度)

【讨论】:

    【解决方案4】:

    对齐是与内存地址相关的属性。简单地说,如果地址 X 与 Z 对齐,则 x 是 Z 的倍数,即 X = Zn+0。这里重要的是 Z 始终是 2 的幂。

    对齐是内存地址的一个属性,表示为数字地址模 2 的幂。对于 例如,地址 0x0001103F 模 4 为 3。据说该地址与 4n+3 对齐,其中 4 表示 选择 2 的幂。地址的对齐方式取决于选择的 2 的幂。相同的地址模 8 为 7。如果地址对齐为 Xn+0,则称该地址与 X 对齐。

    以上语句可在 microsoft c++ reference 中找到。

    如果数据项以与其大小对齐的地址存储在内存中,则该数据项被称为自然对齐,否则未对齐。 例如:如果一个大小为4字节的整型变量存储在一个与4对齐的地址中,那么我们可以说该变量是自然对齐的,即该变量的地址应该是4的倍数。

    编译器总是尽量避免错位。对于简单的数据类型,地址的选择使其是变量大小(以字节为单位)的倍数。编译器还会在结构的情况下进行适当的填充,以实现自然对齐和访问。这里结构将对齐到结构中不同数据项的最大大小。例如:

        struct abc
       {
            int a;
            char b;
       };
    

    这里的结构体 abc 对齐到 4,这是 int 成员的大小,明显大于 1 字节(char 成员的大小)。

    对齐

    此说明符用于将用户定义的类型(如结构、类等)与特定值对齐,该值是 2 的幂。

    对齐

    这是一种运算符,用于获取与结构或类类型对齐的值。 例如:

    #include <iostream>
    struct alignas(16) Bar
    {
        int i; // 4 bytes
        int n; // 4 bytes
        short s; // 2 bytes
    };
    int main()
    {
        std::cout << alignof(Bar) << std::endl; // output: 16
    }
    

    【讨论】:

      【解决方案5】:

      要了解alignasalignof,您必须知道数据对齐是什么意思

      很好的指南https://developer.ibm.com/articles/pa-dalign//

      对齐(简而言之)

      说明一

      数据对齐意味着将数据放入内存中的地址等于字大小的某个倍数。

      说明2

      对齐是内存地址的一个属性,表示为以2的幂为模的数字地址。例如,地址 0x0001103F 模 4 为 3。据说该地址与 4n+3 对齐,其中 4 表示选择的 2 的幂。地址的对齐取决于选择的 2 的幂。相同的地址模 8是 7。如果一个地址的对齐方式是 Xn+0,则称该地址与 X 对齐。

      CPU 执行对存储在内存中的数据进行操作的指令。数据由它们在内存中的地址标识。单个基准也有大小。如果它的地址与其大小对齐,我们称它为自然对齐的数据。否则称为错位。例如,如果用于标识它的地址具有 8 字节对齐,则 8 字节浮点数据自然会对齐。

      好的。您了解“数据对齐” 恭喜!

      什么意思alignas

      说明

      alignas (N) 指定仅将数据放置在 N 的倍数的地址中

      N - 以 2 的幂为模的数字

      语法:

      alignas( the numeric address modulo a power of 2 )
      alignas( alignof(type-id) )
      alignas( type-id )
      

      alignas 说明符可应用于:

      • class / struct / unionenumeration 的声明或定义;

      • 非位域类数据成员的声明

      • 一个变量的声明,除了它不能应用于以下

        • 一个函数参数;
        • catch 子句的异常参数。

      示例:

      struct alignas(256) name1 // every object of type name1 will be aligned to 256-byte boundary
      {
          float test[4];
      };
      
      alignas(128) char name2[128]; // the array "name2" will be aligned to 128-byte boundary
      

      加法1

      alignas 类型说明符是一种可移植的 C++ 标准方法 指定变量和用户定义类型的自定义对齐方式。

      加法 2

      #include <iostream>
      
      struct alignas(16) Bar
      {
          int i;       // 4 bytes
          int n;      // 4 bytes
          alignas(4) char arr[3];
          short s;          // 2 bytes
      };
      
      int main()
      {
          std::cout << alignof(Bar) << std::endl;
      }
      

      当遇到多个 alignas 说明符时,编译器会选择最严格的一个,(值最大的那个)。

      output: 16
      

      加法 3

      alignas 不能用于为类型提供比 type 没有这个声明就会有

      什么意思alignof

      语法:

      alignof( type-id )
      

      返回一个std::size_t类型的值

      同样的定义有sizeof( type-id )

      sizeofalignof 有什么区别?

      struct MyStruct
      {
          int x;
          double y;
          char z;
      };
          
      main() 
      {
          std::cout << "The sizeof(MyStruct): " << sizeof(MyStruct) << std::endl;
          std::cout << "The alignof(MyStruct): " << alignof(MyStruct) << std::endl;
      }
      
      output:
          The sizeof(MyStruct): 24
          The alignof(MyStruct): 8
      

      结构填充

      中的问题

      结构填充是 C 中的一个概念,它添加一个或多个空 内存地址之间的字节以对齐内存中的数据

      更多信息Struct padding in C++

      加法

      结果是一个std::size_t类型的常量表达式,也就是说,它可以 在编译时进行评估。

      更多信息在这里: Source 1, Source 2,

      【讨论】:

        猜你喜欢
        • 2017-06-27
        • 2016-02-12
        • 1970-01-01
        • 2011-10-26
        • 2020-10-10
        • 2019-04-13
        • 2018-08-28
        • 2021-09-14
        • 1970-01-01
        相关资源
        最近更新 更多