Memory Alignment

本文解释了内存对齐的概念及其在提高CPU数据存取效率方面的重要性。通过实例展示了编译器如何通过填充来确保数据能够正确地对齐,进而提高运行效率。
href="file:///C:/DOCUME~1/chendx/LOCALS~1/Temp/msohtml1/01/clip_filelist.xml" rel="File-List" /> href="file:///C:/DOCUME~1/chendx/LOCALS~1/Temp/msohtml1/01/clip_editdata.mso" rel="Edit-Time-Data" />

Memory alignment翻译过来就是内存对齐。举一个简单的例子,int所占内存空间为4byteschar1byte,那么下面这个结构体

struct tMemAlign{

                int i;

                char c[3];

                int k; }

所占的内存空间似乎应该是4+3+4=11bytes。而事实上,在XP sp2Visual Studio .Net 2005的环境下,使用sizeof操作符获得其所占内存空间为12bytes。编译器的这种行为就叫做内存对齐。

什么是内存对齐

内存对齐是为了CPU存取数据更为有效率。软件对内存的读写是以byte为单位的,看上去CPU在运行软件时对内存的读写也是以byte为单位的。而事实上并非如此,只是我们感觉不到罢了。下图列出了32位的CPU内存读取的方式,可以将其推广到16位和64位:

其中Data是数据总线,Address是地址总线,而右上角是内存,一个长方形格子表示1byte。从图中可以看出,32CPU存取数据是以4bytes(32bits)为单位(16位以2bytes64位以8bytes),而不是1byte。也就是说,若是要读入1bytechar数据,CPU也要读入4bytes,然后选出需要的数据。问题就来了,若是一个int型的数据的起始位置为地址1,那么它将延续到地址4。那么CPU要想读入该数据,就必须进行两次内存读取。首先取出地址0-3,然后取出地址4-7,最后提取并组合出正确的int数据,舍弃无关的数据。这无疑降低了CPU运行的效率。于是,让小于等于4bytes的数据(32CPU)位于一组4bytes中,从而能够让CPU对其进行一次读取的方法就是memory alignment。实际上,编译器采取的办法就是padding。比如上面的例子,因为int型数据k若是紧跟着char型数据后存储,那么k就将位于两组4bytes中,于是编译器在padding了最后一个char数据后面紧跟的字节,让k从下一组4bytes的起始位置处开始存储。这就是memory alignment的实现。具体的来说,在Windows下当数据所存储的内存位置是数据字节数的整数倍时,就说该数据是内存对齐的。可以想象,这实际上就是保证了数据只位于一组4bytes中。例如,WORD值应该总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始。

如何进行内存对齐

Windows平台下,cl.exe的对齐选项有/Zp[1|2|4|8|16] /Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。也就是:min ( sizeof(member), n)实际上,1字节边界对齐也就表示了结构成员之间没有padding。注意,/Zpn选项是应用于整个工程的,影响所有的参与编译的结构。而在VC中,动态更改内存对齐方式的办法是使用

#pragma pack(n)

其中,n指示编译器按照n字节边界对齐。若n不存在,则表示取消自定义字节对齐方式。

总结

从上面的说明可以看出,在x86构架中所谓的memory alignment实际上就是一种以空间换时间的手段(Arm中则不然,若不对齐可能会出错)。不过这些不进行memory alignment所带来的时间上的损失对效率的影响是细微的。如果我们可以忍受多态和结构化异常所带来的损失,却在这些细微的地方斤斤计较,未免太过矫情了。
内存对齐是操作系统为了快速访问内存而采取的一种策略,它涉及变量在内存中的排列规则,以避免变量的二次访问。其原理、作用及应用如下: - **原理**:许多计算机系统对基本数据类型的可允许地址作了一定限制,要求某种类型对象的地址必须是某个值n(通常是2、4、8)的倍数,从而简化处理器和存储器之间接口的硬件设计。例如Linux的对齐策略是2字节数据类型(如short)的地址必须是2的倍数,较大的数据类型(如int、int*、float、double)则必须是4的倍数;Microsoft Windows要求更为严格,任何k字节对象的地址必须是k的倍数。现代计算机中内存空间按byte划分,访问特定类型变量时通常在特定的内存地址进行,这就需要各种类型数据按照一定规则在空间上排列,而非顺序排放 [^2][^3]。 - **作用**: - **提高访问效率**:各个硬件平台对存储空间的处理存在差异,一些平台对某些特定类型的数据只能从特定地址开始存取。若不进行对齐,存取效率会降低。例如,有些平台每次读从偶地址开始,一个32位的int型数据存放在偶地址开始处,一个读周期就能读出;若存放在奇地址开始处,则需2个读周期,还需对两次读出结果的高低字节进行拼凑才能得到该32bit数据。数据对齐后,可减少CPU的访存次数,避免一个数据被拆分到两个或多个存储单元中而造成多次访问内存 [^3][^4]。 - **简化硬件设计**:使处理器和存储器之间接口的硬件设计更简单,因为对齐的内存访问仅需一次访问,而访问未对齐的内存,处理器需要进行两次内存访问 [^2]。 - **应用**: - **数据结构设计**:在设计数据结构(尤其是栈)时,应尽可能地在自然边界上对齐,以提高内存访问效率。 - **跨平台开发**:不同操作系统(如Linux和Microsoft Windows)有不同的对齐策略,在跨平台开发中需要考虑内存对齐,确保数据在不同平台上都能正确存储和访问。 ```python import sys # 定义一个包含不同数据类型的类 class Example: def __init__(self): self.a = 1 # int 类型 self.b = 2.0 # float 类型 self.c = 'c' # char 类型 # 创建对象 example = Example() # 打印对象占用的内存大小 print(sys.getsizeof(example)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值