内存对齐

作者: this_is_for_u | 来源:发表于2020-03-01 16:29 被阅读0次

    什么是内存对齐

    理论上计算机对于任何变量的访问都可以从任意位置开始,然而实际上系统会对这些变量的存放地址有限制,通常将变量首地址设为某个数N的倍数,这就是内存对齐。

    为什么要内存对齐

    1. 硬件平台限制,内存以字节为单位,不同硬件平台不一定支持任何内存地址的存取,一般可能以双字节、4字节等为单位存取内存,为了保证处理器正确存取数据,需要进行内存对齐。
    2. 提高CPU内存访问速度,一般处理器的内存存取粒度都是N的整数倍,假如访问N大小的数据,没有进行内存对齐,有可能就需要两次访问才可以读取出数据,而进行内存对齐可以一次性把数据全部读取出来,提高效率。

    内存对齐规则

    1. 数据成员对齐规则:struct或者union的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员都按照#pragma pack数值和这个数据成员自身大小中更小的那个进行对齐。
    2. 整体对齐规则:struct或者union的首地址按照内部最大数据成员的大小和#pragma pack数值较小的那个N进行对齐,并且结构体的总大小为N的整数倍,如有必要编译器也会在最后一个成员后面填充一些字节用于对齐。

    如何进行内存对齐

    class A
    {
        int a;
        char d;
    };
    
    // 创建给定类型对象大小满足对齐要求的未初始化内存块,在一个内存对齐的缓冲区上创建对象
    // C++11后可以这样操作
    void align_cpp11_after()
    {
        static std::aligned_storage<sizeof(A),
                                    alignof(A)>::type data;
        A *attr = new (&data) A;
    }
    
    // C++11之前
    void align_cpp11_before()
    {
        static char data[sizeof(void *) + sizeof(A)];
        const uintptr_t kAlign = sizeof(void *) - 1;
        char *align_ptr =
            reinterpret_cast<char *>(reinterpret_cast<uintptr_t>(data + kAlign) & ~kAlign);
        A *attr = new (align_ptr) A;
    }
    

    std::aligned_storage

    std::aligned_storage用于创建一块对齐的内存,具体实现如下

      template<std::size_t _Len>
        struct __aligned_storage_msa
        {
          union __type
          {
        unsigned char __data[_Len];
        struct __attribute__((__aligned__)) { } __align;
          };
        };
    
      /**
       *  @brief Alignment type.
       *
       *  The value of _Align is a default-alignment which shall be the
       *  most stringent alignment requirement for any C++ object type
       *  whose size is no greater than _Len (3.9). The member typedef
       *  type shall be a POD type suitable for use as uninitialized
       *  storage for any object whose size is at most _Len and whose
       *  alignment is a divisor of _Align.
      */
      template<std::size_t _Len, std::size_t _Align =
           __alignof__(typename __aligned_storage_msa<_Len>::__type)>
        struct aligned_storage
        {
          union type
          {
        unsigned char __data[_Len];
        struct __attribute__((__aligned__((_Align)))) { } __align;
          };
        };
    

    使用方式上面已经介绍过了,Align大小也可以不填,默认会是采用最大最有益的对齐大小,大家可能源码里有些语句不了解含义,如下:

    __attribute__((packed))告诉编译器取消编译中的内存对齐优化,采用实际占用的字节数进行对齐
    __attribute__((aligned(N))) 告诉编译器在编译过程中按照N字节对齐,经过测试这个N只有大于结构体中最大的变量的大小才有用
    __attribute__((aligned)) 后面不接数字,告诉编译器根据目标机制采用最大最有益的方式对齐,基本上就是16字节对齐
    alignof(X) 返回某类型的对齐大小,与std::alignment_of类似,这两个功能完全相同,但是std::alignment_of可以用于模板元编程

    类大小的测试

    普通类型代码如下:

    #include <stdio.h>
    // g++空结构体的内存大小为1,需要分配1字节用于占位,C++编译器不允许对象为0长度,无法获取地址等
    // gcc中为0
    struct A1
    {
    };
    
    struct A2
    {
        ;
    };
    
    struct A3
    {
        char a;               // 1
        int b;                // 4
        unsigned short c;     // 2
        long d;               // 8
        unsigned long long e; // 8
        char f;               // 1
    };
    // A3大小为1+(3)+4+2+(6)+8+8+1+(7)=40,括号内是为了内存对齐加的padding大小
    
    struct A4
    {
        int b;
        char a;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    };
    // A4大小为4+1+(1)+2+8+8+1+(7)=32
    
    //pragma pack(n)
    //alignment must be a power of two
    #pragma pack(2) //指定按两字节对齐
    struct B1
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    };
    // B1大小为1+(1)+4+2+8+8+1+(1)=26
    #pragma pack() //取消指定对齐
    
    #pragma pack(4)
    struct B2
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    };
    // B2大小为1+(3)+4+2+(2)+8+8+1+(3)=32
    #pragma pack()
    
    #pragma pack(8)
    struct B3
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    };
    // B3大小为1+(3)+4+2+(6)+8+8+1+(7)=40
    #pragma pack()
    
    #pragma pack(16)
    struct B4
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    };
    // B4大小为1+(3)+4+2+(6)+8+8+1+(7)=40
    #pragma pack()
    
    //__attribute__
    struct C1
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned));
    // C1大小为1+(3)+4+2+(6)+8+8+1+(15)=48
    
    struct C2
    {
        char a;
        int b;
        unsigned short c;
        //long d;
        //unsigned long long e;
        char f;
    } __attribute__((aligned));
    // C2大小为1+(3)+4+2+(6)=16
    
    struct C3
    {
        char a;
        int b;
        unsigned short c;
        long d;
        //unsigned long long e;
        char f;
    } __attribute__((aligned));
    // C3大小为1+(3)+4+2+(6)+8+(8)=32
    
    struct C4
    {
        char a;
        //int b;
        unsigned short c;
        //long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned));
    // C4大小为1+(1)+2+(4)+8+1+(15)=32
    
    struct C5
    {
        char a;
        //int b;
        //unsigned short c;
        //long d;
        //unsigned long long e;
        //char f;
    } __attribute__((aligned));
    // C5大小为1+(15)=16
    
    struct D1
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned(1)));
    // D1大小为1+(3)+4+2+(6)+8+8+1+(7)=40
    
    struct D2
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned(4)));
    // D2大小为1+(3)+4+2+(6)+8+8+1+(7)=40
    
    struct D3
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned(8)));
    // D3大小为1+(3)+4+2+(6)+8+8+1+(7)=40
    
    struct D4
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((aligned(16)));
    // D4大小为1+(3)+4+2+(6)+8+8+1+(15)=48
    
    struct E1
    {
        char a;
        int b;
        unsigned short c;
        long d;
        unsigned long long e;
        char f;
    } __attribute__((packed));
    // E1大小为1+4+2+8+8+1=24
    
    // __attribute__((packed))告诉编译器取消编译中的内存对齐优化,采用实际占用的字节数进行对齐
    // __attribute__((aligned(N))) 告诉编译器在编译过程中按照N字节对齐,经过测试这个N只有大于结构体中最大的变量的大小才有用
    // __attribute__((aligned)) 后面不接数字,告诉编译器根据目标机制采用最大最有益的方式对齐,基本上就是16字节对齐
    // alignof(X) 返回某类型的对齐大小,与std::alignment_of类似,这两个功能完全相同,但是std::alignment_of可以用于模板元编程
    
    int main()
    {
        printf("sizeof(char):%2d...sizeof(int):%2d...sizeof(ushort):%2d...sizeof(long):%2d...sizeof(ulonglong):%2d\n\r",
               sizeof(char), sizeof(int), sizeof(unsigned short), sizeof(long), sizeof(unsigned long long));
    
        printf("sizeof(A1):%2d...sizeof(A2):%2d...sizeof(A3):%2d...sizeof(A4):%2d\n\r",
               sizeof(struct A1), sizeof(struct A2), sizeof(struct A3), sizeof(struct A4));
    
        printf("sizeof(B1):%2d...sizeof(B2):%2d...sizeof(B3):%2d...sizeof(B4):%2d\n\r",
               sizeof(struct B1), sizeof(struct B2), sizeof(struct B3), sizeof(struct B4));
    
        printf("sizeof(C1):%2d...sizeof(C2):%2d...sizeof(C3):%2d...sizeof(C4):%2d...sizeof(C5):%2d\n\r",
               sizeof(struct C1), sizeof(struct C2), sizeof(struct C3), sizeof(struct C4), sizeof(struct C5));
    
        printf("sizeof(D1):%2d...sizeof(D2):%2d...sizeof(D3):%2d...sizeof(D4):%2d\n\r",
               sizeof(struct D1), sizeof(struct D2), sizeof(struct D3), sizeof(struct D4));
    
        printf("sizeof(E1):%2d...\n\r", sizeof(struct E1));
    
        return 1;
    }
    
    g++编译输出结果如下:
    sizeof(char): 1...sizeof(int): 4...sizeof(ushort): 2...sizeof(long): 8...sizeof(ulonglong): 8
    sizeof(A1): 1...sizeof(A2): 1...sizeof(A3):40...sizeof(A4):32
    sizeof(B1):26...sizeof(B2):32...sizeof(B3):40...sizeof(B4):40
    sizeof(C1):48...sizeof(C2):16...sizeof(C3):32...sizeof(C4):32...sizeof(C5):16
    sizeof(D1):40...sizeof(D2):40...sizeof(D3):40...sizeof(D4):48
    sizeof(E1):24...
    

    具体的计算方式在代码中已经添加注释

    带有虚函数的类的大小测试:

    #include <iostream>
    
    using namespace std;
    
    class A1
    {
        int a;
        int b;
        int c;
        char d;
        void f()
        {
            cout << "f";
        }
    };
    
    class A2
    {
        int a;
        int b;
        int c;
        char d;
    };
    
    class A3
    {
        int a;
        int b;
        int c;
        char d;
        virtual void f()
        {
            cout << "f";
        }
    };
    
    int main()
    {
        cout << "sizeof A1 " << sizeof(A1) << endl;
        cout << "alignof A1 " << alignof(A1) << endl;
        cout << "sizeof A2 " << sizeof(A2) << endl;
        cout << "alignof A2 " << alignof(A2) << endl;
        cout << "sizeof A3 " << sizeof(A3) << endl;
        cout << "alignof A4 " << alignof(A3) << endl;
    
        return 0;
    }
    
    编译运行结果如下:
    sizeof A1 16
    alignof A1 4
    sizeof A2 16
    alignof A2 4
    sizeof A3 24
    alignof A4 8
    

    通过结果可知,一个类带有虚函数,类的大小会多8个字节,这8个字节是虚函数表指针的大小,指针类型为长整型long,在32位机器上是4字节,64位机器上是8字节。

    参考链接

    align_storage
    内存对齐百度百科
    alignof运算符
    __attribute__简介
    对齐规则
    https://stackoverflow.com/questions/36981968/stdalignment-of-versus-alignof
    https://zh.cppreference.com/w/cpp/types/alignment_of

    关注一下呗

    二维码.jpg

    相关文章

      网友评论

        本文标题:内存对齐

        本文链接:https://www.haomeiwen.com/subject/dolfkhtx.html