现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

内存对齐之深度探索

2012-11-24 20:35 工业·编程 ⁄ 共 1424字 ⁄ 字号 暂无评论

编译器为什么要替我们内存对齐?

学了计算机组成原理,了解了内存的基本单元是一个字节,内存可以随机寻址,于是乎我天真的认为内存就是一个字节型的容器,基本单位是单个字节。
 
Figure 1. 我眼中的内存空间布局
How Programmers See Memory
 
悲剧的是,内存读写的真正访问者cpu不是这么想的。cpu是根据内存访问粒度(memory access granularity,下文简写成MAG)来读取内存,MAG就是cpu一次内存访问操作的数据量,具体数值依赖于特定的平台,一般是2byte、4byte、8byte。
 
Figure 2. cpu眼中的内存空间布局
How Some Processors See Memory
 
因此,程序员和cpu看待内存空间布局是存在差异的。唉,既不能过多的苛刻程序员,又要让cpu舒服,怎么办呢?只好让编译器来对我们的代码进行隐式的内存对齐(当然它能做的只是帮程序中的数据进行内存对齐,至于直接用指针去访问内存,它是不会管的)。向开发编译器的大大们致敬!

内存对齐的好处

下面用一个小实例来剖析内存对齐的好处:很简单,在32位的机器下,分别访问一个内存对齐的地址空间(从地址0开始)和一个没有对齐的地址空间(从地址1开始),读取四个字节到cpu的寄存器中,比较两者的读取过程。
Case1:内存访问粒度为1个字节(cpu眼中的内存模型等价于程序员眼中的内存模型):
 
Figure 3. MAG=1
Single-byte memory access granularity
 
Result:读取4个字节,两者都需要进行4次内存访问操作。打平,在MAG=1的情况下不需要考虑内存对齐。
 
Case2:内存访问粒度为2个字节:
 
Figure 4. MAG=2
Double-byte memory access granularity
 
Result:读取4个字节,左边的(内存对齐地址)只需要进行2次内存访问操作,右边的需要进行3次内存访问操作+附加操作(见下文)。内存对齐地址取胜!
 
Case3:内存访问粒度为4个字节:

Figure 5. MAG=4
Quad-byte memory access granularity

Result:读取4个字节,左边的只需要进行1次内存访问操作,右边的需要进行2次内存访问操作+附加操作。内存对齐地址再次取胜!
 
Conclusion:
内存对齐地址vs没有内存对齐的地址,在三种不同的内存访问粒度下,取得了2胜一平的完胜战绩。对于32位的机器,实际的内存访问粒度是4个字节,原因如下
  • 每一次内存访问操作需要一个常量开销;
  • 在数据量一定的情况下,减少内存访问操作,能提高程序运行性能;
  • 增大内存访问粒度(当然不超过数据总线的带宽),能减少内存访问操作(从上面的实例就能够看出来);
一句话,内存对齐确实可以提高程序性能。

cpu如何处理没有内存对齐的数据访问?

继续分析上面那个实例,在内存访问粒度为2、从地址1开始读取四个字节的cpu处理过程(硬件方式):
  1. 读取数据所在的第一块内存空间(0-1),移除多余字节(0);
  2. 读取数据所在的第二块内存空间(2-3);
  3. 读取数据所在的第三块内存空间(4-5),移除多余字节(5);
  4. 把三块数据拼接起来(1-4),放入寄存器中。
访问一块相同大小的数据,内存对齐的优势是多么的巨大!

如果cpu能这么来处理,也只不过是影响了我们程序的运行性能,至少还是能运行的!悲剧的是,以前的cpu并没有这么“勤快”,遇到没有内存对齐的数据访问,它会直接抛出一个异常:操作系统可能会响应这个异常,用软件的方式来处理,性能只会更差;或者程序直接崩溃掉。一句话,内存对齐的代码确实具有更高的可移植性!
 
Over!更多内容,参照此文:https://www.ibm.com/developerworks/library/pa-dalign/

给我留言

留言无头像?