内存对齐之深度探索

(废话:最近接触到内存对齐概念,针对内存对齐的规则,我在vc中一一进行了验证,发现编译器确实对我的数据进行了内存对齐。我便知道内存对齐必定是有它的优点所在,是什么呢?我找的资料都是说内存对齐可以提高内存访问效率、可移植到不同的平台等等,但是为什么会这样呢,没有一个清晰的解释。下面便是我多天努力的成果!)


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


学了计算机组成原理,了解了内存的基本单元是一个字节,内存可以随机寻址,于是乎我天真的认为内存就是一个字节型的容器,基本单位是单个字节。

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/
内存对齐的具体规则,参照此篇博客:http://blog.csdn.net/liu1064782986/article/details/7600979
posted @ 2012-05-29 14:22  刘军newhand_liu  阅读(253)  评论(0编辑  收藏  举报