• 新增内存对齐关键字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

  • 相关阅读:
    Java面试之对象拷贝
    Java面试之反射
    Java面试之多线程
    Java面试之容器
    Java面试之基础一
    Java面试之Hibernate
    Mysql进阶
    高并发,不怕不怕「限流算法第一把法器:计数器法」
    SpringBoot加载速度慢
    idea VM options参数优化
  • 原文地址:https://www.cnblogs.com/klb561/p/14858825.html
Copyright © 2020-2023  润新知