CPU读写内存的单位是一个机器字。
内存可以看成一个byte数组,我们对这个'大数组'中的每个元素进行读写,
比如在C中我们可以用指针一次读写一个或者更多字节,这是我们一般程序员眼中的内存。
但是从CPU角度看,CPU是按照'块(chunk)'来读写内存的,
块的大小可以是2bytes, 4bytes, 8bytes, 16bytes甚至是32bytes。
这个CPU访问内存时采用的块的大小,我们可以称为'内存访问粒度'。
程序员眼中的内存:1
2
3
4-----------------------------------------
| | | | | | | | | | | | | | | |
-----------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)
CPU眼中的内存:(以粒度=4为例)1
2
3
4--------------------------------------------------
| | | | | | | | | | | | | | | |
--------------------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)
如果'内存访问粒度'为1,CPU从地址0开始读取,需要4次访问才能将4个字节读到寄存器中;
同样如果'内存访问粒度'为1,CPU从地址1开始读取,也需要4次访问才能将4个字节读到寄存器中;
而且对于这种理想中的'内存访问粒度'为1的CPU,所有地址都是'aligned address'。
如果'内存访问粒度'为2,CPU从地址0开始读取,需要2次访问才能将4个字节读到寄存器中;
每次访存都能从'aligned address'起始。
如果'内存访问粒度'为2,CPU从地址1开始读取,相当于内存中数据分布在1,2,3,4三个地址上,
由于1不是'aligned address',所以这时CPU要做些其他工作,由于这四个字节分布在三个chunk上,
所以CPU需要进行三次访存操作,
第一次读取chunk1(即地址0,1上两个字节,而且仅仅地址1上的数据有用),
第二次读取chunk2(即地址2,3上两个字节,这两个地址上的数据都有用),
最后一次读取chunk3(即地址5,6上两个字节,而且仅仅地址5上的数据有用),
最后CPU会将读取的有用的数据做merge操作,然后放到寄存器中。
同理可以推断如果'内存访问粒度'为4,那么从地址1开始读取,需要2次访问,访问后得到的结果merge后放到寄存器中。
是不是所有的CPU都会帮你这么做呢,当然不是。
有些厂商的CPU发现你访问unaligned address,就会报错,或者打开调试器或者dump core,
比如sun sparc solaris绝对不会容忍你访问unaligned address,都会以一个core结束你的程序的执行。
所以一般编译器都会在编译时做相应的优化,
以保证程序运行时所有数据都是存储在'aligned address'上的,这就是内存对齐的由来。
在'Data alignment: Straighten up and fly right'这篇文章中作者还得出一个结论
那就是:"如果访问的地址是unaligned的,那么采用大粒度访问内存有可能比小粒度访问内存还要慢"。
参考资料
http://www.cnblogs.com/wanghao111/archive/2009/08/29/1556436.html
http://www.ibm.com/developerworks/library/pa-dalign/