zoukankan      html  css  js  c++  java
  • 新增内存对齐关键字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

  • 相关阅读:
    Delphi XE4 FireMonkey 开发 IOS APP 发布到 AppStore 最后一步.
    Native iOS Control Delphi XE4
    Delphi XE4 iAD Framework 支持.
    using IOS API with Delphi XE4
    GoF23种设计模式之行为型模式之命令模式
    Android青翼蝠王之ContentProvider
    Android白眉鹰王之BroadcastReceiver
    Android倚天剑之Notification之亮剑IOS
    Android紫衫龙王之Activity
    GoF23种设计模式之行为型模式之访问者模式
  • 原文地址:https://www.cnblogs.com/klb561/p/14858825.html
Copyright © 2011-2022 走看看