系统相关
首页 > 系统相关> > 新增内存对齐关键字alignas、alignof 用法

新增内存对齐关键字alignas、alignof 用法

作者:互联网

alignas关键字用来设置内存中对齐方式,最小是8字节对齐,可以是16,32,64,128等。

alignas用来指定对象的对齐字节数。效果和__attribute__((aligned(n)))一样

alignof用来查看对象的对齐字节数。用法类似于sizeof

https://blog.csdn.net/weixin_38956024/article/details/112773581

问答环节:

问题1) 什么是对齐。
举例说明,某个int类型的对象,要求其存储地址的特征是4的整倍数。例如0x0000CC04。我们把地址值0x0000CC04除以4,余数0,那么这个对象的地址就是对齐的。

问题2) 为什么要对齐。
举例说明,对于int数据,硬件只能在4的倍数的地址读写,假设某int对象的地址是0x0000CC06,则硬件先读取0x0000CC04开始的4个字节,
取其0x0000CC06, 0x0000CC07。硬件然后读取0x0000CC08开始的4个字节,取其0x0000CC08, 0x0000CC09。将两次读取的有用信息拼接即可。
显然,效率不高。更严重的,硬件会报错,程序执行不下去。

问题3) x86体系下,用#pragma pack(1) 改变结构体中int成员的对齐属性,也没报错呀
只能说x86容忍性高,程序正常跑,不见得效率没有降低。

 

问题4) C++11的alignas是什么。

改变一个数据类型的对齐属性。在例子中,Test::a的对齐值变成16,意味着a的地址值必须能被16整除。
考察a的偏移值是16,意味着arr[9]后面被插入填充用的7个字节了。
问题5) 上例中,只是a相对结构体首地址的偏移值16,如果结构体首地址的值是0x0000CC01,然后+16 = 0x0000CC11,显然不满足地址值的16倍数对齐了
在哪里创建Test当然是很重要的,为了防止上述事件发生,需要编译器和程序员的共同努力,但主要担子还在编译器上。例如在函数栈上创建一个Test对象,编译器必须选择一个好地方才行。
问题6) 为什么sizeof(Test)是48
offsetof(a)=16,a本身长4字节,b的偏移本应是20。
但是考虑到b的类型是double,其默认对齐值是8。20不是8的倍数,填充4个垃圾字节,现在到达偏移值24。
所以b的真正偏移值是24,b占8个字节。
现在到达c,c的偏移值是32,c本身占1个字节,整个test的长度貌似是33。
可是你要考虑test数组,例如数组test kk[2]。kk[1].a, 相对于数组首地址的偏移为33+16=49。这个地址不满足a的对齐了。
但是在c的后面填充15个垃圾字节,则 kk[1].a的地址 =  kk的首地址值 + kk[0]长度48 + kk[1].a偏移值16

假设编译器把kk的首地址值放置的位置使:   
kk的首地址值  /  16 = 0
kk[0]长度48 / 16 = 0
kk[1].a偏移值16 / 16 = 0
则  kk[1].a的地址 / 16 = 0

问题7)  为什么alignof(Test)是16
由于对齐值只能是2,4,8等2的幂,所以大的对齐值一定满足小的对齐需求。例如我按照16字节对齐了,当然也满足8字节对齐,4自己对齐,2字节对齐,1字节对齐了。
整个结构体的对齐值,就是各成员对齐值,最大的那个。
问题8)alignas(2) int a; 可以实现吗?
C++11规定,只能放大对齐值,而int的原始对齐值是4,现在你要求按2对齐,编译器会忽略你的请求。


struct alignas(64) Test{
//virtual ~Test(){}
char arr[9];
alignas(16) int a;
double b;
char c;
};
问题9) 现在的test对齐值是多少?
要求Test的地址能被64整除,那么Test本初的原始对齐值16,被弃用,使用更大的对齐值64。

问题10) 现在的test大小是多少?
Test的大小是64,增加了更多的填充垃圾字节,以适应64倍数地址值。

问题11) C++11为什么增加这个机制,让程序员控制对齐方式。
1是现有编译器都有语言扩展,例如__declspec(align(n))等,急待统一。
2是现实需求,例如利用placement new语法创建对象,如果你随便提供的内存块同T类型的对齐要求不一致,就是有副作用。
3是语言本身完善的必然,缺了这个东西,C++就不是完备的。C++是如此学院派,价值取向就是相容,独立,完备。

不用数据对齐方式test1占用16个字节,为什么?因为计算机会做字节对齐,一般都是对齐8位,如果不用alignas关键字,默认一般是8位对齐,但也有机器不是8位对齐。

test2占用16字节,对齐方式alignas(8)

test3占用16字节,对齐方式alignas(16)

test4占用32字节,对齐方式alignas(32)

计算方法就是对齐数的整数倍,比如32位对齐,实际数据大小不到32字节,但内存还是占用32字节。实际数据大于32字节小于64字节,内存占用64字节。

这种明确规定占用字节大小后,编写代码将更具有跨平台性。

 

Alignas可以更加严格地控制CPU缓存上对象布局的方式,从而更快地访问对象.最佳使用的目标如下,这是使用alignas的用例

>希望避免从缓存行中不必要的数据失效
>希望优化CPU读取,以便节省CPU周期的浪费.

如何使用alignas对齐缓存行有帮助
使用1 – 避免从缓存行中不必要的数据失效
您可以使用alignas来保持单独线程使用的地址或对象在不同的​​高速缓存行上运行,这样一个线程就不会无意中使另一个核心的高速缓存行无效.

怎么回事:
考虑当进程中的线程在核心0上运行并写入地址xxxx时的情况.此地址现在加载到核心0的L1缓存中.
线程号2访问地址xxxx n个字节.现在,如果这两个地址碰巧都在同一个高速缓存行上,那么线程2的任何写入都将不必要使核心0的高速缓存行无效.因此,线程0被延迟,直到高速缓存行无效并再次加载.这妨碍了多线程环境中的性能.

使用2
将对象与单独的缓存行对齐,以使对象不会分布在多个缓存行中.这节省了CPU周期.例如.如果您的对象大小是例如. 118字节,最好将其与64字节对齐,因为在大多数处理器上,缓存行大小现在是64字节.

如果不这样做,则可以在64字节高速缓存行上按如下方式布置对象. (例如,对象具有118字节的实际大小并且具有自然对齐,大小变为4的倍数,因此120字节)

 

Cache line 1<—–Object 1 60Bytes –> <—your Object 4> Bytes ———->
Cache line 2<——— Your object 64 Bytes ———————————>
Cache line 3 <—– Your object 52 bytes —–> <— Some other object 12 Bytes –>

由于CPU读取多个缓存行,因此您的对象将在3个CPU周期中读取.如果要优化它,请考虑使用alignas(64).这样,您的对象将始终分布在2个缓存行上.

注意事项
请注意,在考虑对齐之前,您需要仔细检查对象.原因是错误的方法会导致更多的填充,从而更多地浪费L2缓存.有一些简单的技术可以按顺序排列数据成员,从而避免浪费.

参考 相关文章:

  1. C++11语言auto关键字作用
    C++11中对关键字auto进行了重新定义,可以让编译器根据初始值类型自动推断变量的类型。auto a  = 0;   //推断出a类型为int atuo b = 0.0;  //推断出b类型doublu auto c = 'A';  //推断出c类型是char如果不知道为什么能自动推断出来可以看下:常量类型     和   C++常量类型 获取上面写的都是推断基本类型,当然C++11 auto关键字并非只样简单的运用,更多的是为了自定义类型推断。相信很多人在写迭代器时会很头痛,太长了。先个vector类型的迭代器。...
  2. C++11关键字decltype用途
    关键字decltype将变量的类型声明为表达式指定的类型。下面的语句的含义是,让y的类型与x相同,其中x是一个表达式。decltype (x) y;decltype的工作原理比auto复杂,可以指定类型为引用和const。...
  3. C++11新增关键字nullptr
    nullptr关键字就是表示空指针的意思,C++11之前空指针都是NULL。已经有了NULL哪为什么还要有nullptr关键字? nullptr表示空指针,不能转换为整型类型。为了向后兼容,C++11仍允许用0(NULL)来表示空指针。建议使用nullptr而不是0(NULL),因为更高的类型安全。...
  4. switch作用
    switch语句允许你通过一个表达式判断许多数值,它一般用来在多重循环中代替if()...else if()...else if()... 语句. break 语句必须在每个case 语句之后, 负责循环将执行所有的case语句.default case 是可选的.假如所有的case都不能匹配的话,他将和defult case匹配. 例如:...
  5. struct作用
    Structs 类似于 classes, struct中的成员更像是类中的公共成员.在C中,structs 仅能包含数据并不允许有继承表. 例如:...
  6. namespace作用
    关键字namespace允许你创建一个新的空间.名字由你选择,忽略创建没有命名的名字空间.一旦你创建了一个名字空间,你必须明确地说明它或者用关键字using....
  7. union作用
    Unions 类似于 classes,除了所有的成员分享同一内存外它的缺省值更像公共类型. 例如:...
  8. C++11模板别名
    类型取别名,可以用typedef关键字,一般将类型名比较长的,再取个别名,在使用时会更方便。写几个用typedef关键字取的别名:...
  9. 类型转换函数作用详解
    先来看一段代码: circle c1; string strc21 = c1; //这句编译通不过 cout...
  10. C++11新增类型
    C++11新增类型long long和unsigned long long,以支持64位(或更宽)的整型;新增为型char16_t和char32_t,以支持16位和32位的字符表示。

alignas用来指定对象的对齐字节数。效果和__attribute__((aligned(n)))一样
alignof用来查看对象的对齐字节数。用法类似于sizeof
二、实验使用alignas修改结构体的对齐字节数,然后用alignof查看是否设置成功。
同时,与__attribute__((aligned(n))) 进行对比。
#include <iostream>
using namespace std;
struct alignas(1) student1{char a;int b;float c;};
struct alignas(16) student2{char a;int b;float c;};
struct student3{char a;int b;float c;}__attribute__((aligned(1)));
struct student4{char a;int b;float c;}__attribute__((aligned(16)));

int main(){cout << alignof(student1) << endl;  //alignas(1)cout << alignof(student2) << endl;//alignas(16)cout << alignof(student3) << endl;//__attribute__((aligned(1)))cout << alignof(student4) << endl;//__attribute__((aligned(16)))return 0;}1234567891011121314151617181920212223242526272829303132333435363738394041
结论:alignas和 __attribute__((aligned(n)))的作用完全一样。但是,它们都只能放大对齐的字节数,而不能缩减。要是我想将对齐的字节数设置为1,该怎么办?
可以使用#pragma pack(n)来设置
#include <iostream>
using namespace std;
#pragma pack(1)struct  student1{char a;int b;float c;};#pragma pack()


int main(){cout << alignof(student1) << endl;  
return 0;}123456789101112131415161718192021末尾记得加上#pragma pack(),表示设置默认的对齐方式。
三、最后感觉alignas是对__attribute__((aligned(n)))的一种简化,让程序更加清爽。————————————————版权声明:本文为CSDN博主「无.处安放的灵魂」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_38956024/article/details/112773581

标签:__,字节,16,alignof,C++,alignas,对齐
来源: https://www.cnblogs.com/klb561/p/14858825.html