编译之--内存对齐

内存对齐

现代计算机中内存空间都是按照 byte 划分的,也就是字节,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐

为什么要内存对齐?

从cpu的角度来说,CPU不会一次读取或写入一个字节的内存。相反,CPU一次以2、4、8、16或32字节块访问内存。这样做的原因是提升性能:
访问4字节或16字节边界上的地址比访问1字节边界上的地址快得多。
下图说明了CPU如何以4字节内存访问粒度访问4字节数据

那么如果数据/内存没有对齐的话,则CPU必须执行额外的工作才能访问数据,如下图,CPU一次性访问4个字节:

没有对齐的话就会经历:

装入2个数据块,移出不需要的字节,然后将它们组合在一起。此过程肯定会降低性能,浪费CPU周期,只是为了从内存中获取正确的数据。

那么现在的话,对齐这部分编译器会帮我搞定,一般不需要自己动手,但也排除一些极端情况。在32位或者64位x86系统中,对齐方式与其数据类型的大小基本相同。编译器在其自然长度边界上对齐变量。 CPU就可以正确处理未对齐的数据。
举一个简单的例子:
我们知道,int类型占4个字节的空间,char一般就是1个,如果把它们放在一个结构体中,理论上占 4+1=5个byte,但由于cpu的访问和对齐的原因,实际上,默认情况下它会占8个byte,可以sizeof以下就知道了

struct User{
    int mid;
    char gender;
};

原理如图:

为什么要做内存对齐?

  • 平台原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。————- 比如,有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐。
  • 性能原因:内存对齐可以提高存取效率。————- 比如,有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。
    引用自 内存对齐

编译器编译时对齐的规则

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。可以通过预编译命令#pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中 n 就是你要指定的“对齐系数”。
如果上面那个例子,对齐系数改成1,那么就只占用5个byte,但这样会使得cpu负担更大

显示的修改内存对齐规则--预编译时的pragma pack

在某些情况下,通过为数据结构指定自定义对齐方式,可以显着提高性能或节省内存.

posted @ 2020-09-15 23:09  real-zhouyc  阅读(311)  评论(0编辑  收藏  举报