新增内存对齐关键字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缓存.有一些简单的技术可以按顺序排列数据成员,从而避免浪费.

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

posted @ 2021-06-07 14:51  konglingbin  阅读(4263)  评论(0编辑  收藏  举报