关于内存对齐的学习笔记

内存对齐的学习笔记
作者:laomai
blog:  
http://blog.csdn.net/laomai

一、问题的提出
       
两年之前我写过一篇可变参数学习笔记,里面曾经简单的解释过一句代码:

        ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))
       
这个宏的作用是在考虑字节对齐的因素下计算变量n占用的字节大小,以便求出下一个可变参数的起始地址。

       
当时限于时间和水平,未能做更详细的解释。

       
今天(2007-11-26)csdn论坛上看到了一个帖子

        http://topic.csdn.net/u/20071123/16/c8d17d3f-9f49-49af-a6d8-1d7a7d84dc1c.html?seed=303711257
问题:CRT源码分析中一个关于可变函数参数的问题
   
提问者
:Sun_Moon_Stars
这个帖子里面又问到了这个宏,于是决定抽出半天时间,把这个问题详细的说清楚。也算是把我的那篇文章做一个完美的结尾。
 
    
二、引子

     先看一个日常生活中的问题,

问题1:假设有要把一批货物放到集装箱里,货物有12件, 一个箱子最多能装6件货物,求箱子的数目。

解答:显然我们需要12/6=2个箱子,并且每个箱子都是满的。这个连小学生都会算
:-)

问题2:  把问题1的条件改一下,假设一个箱子最多能装5件货物,那么现在的箱子数是多少?

解答: 12/5=2.4个,但是根据实际情况,箱子的个数必须为整数,(有不知道这个常识的就不要再往下看了,回小学重读吧,呵呵)自然我们就要取3

     
下面把上面的装箱问题一般化。


三、一般数学模型

问题3:设一个箱子最多可以装M件货物,现有N件货物,问需要多少个箱子,要给出一般的计算公式。

这里要注意两点:

1
、箱子的总数必须为整数;

2
N不一定大于M很显然,即使N <M,也得需要一只箱子
             

四、通项公式

1
、预备知识

在讨论问题3的解答之前,我们先明确一下/运算符的含义。

定义/运算为求商运算,即

对任意两个整数N,M,必然有且只有唯一的整数X,满足

X*M   <=   N   <   (X+1)*M
,那么记N/M=X   (定义1

这个也正是c/运算符的确切含义。x的存在性和唯一性的严格证明可以见数论教材。
以后如无额外说明,/运算的含义均和本处一致。 另外,根据实际情况,

后面出现的所有大写字母如MNABXYZ等均表示正整数也就是自然数。

举几个/运算的例子:

(1)  (M-1)/M=0,                (等式1

一般的,一个比M小的自然数对M取商的结果一定是0

(2)N满足M<=N<2*M,则N/M=1             (等式2

(3)(M*X)/M=X

  


/
运算有一个基本的性质。
N=M*X+Y,则N/M=X+Y/M,证明略。


注意:这个公式的含义是/运算在一定条件下满足分配律。

N不是可以随便拆的,设N=A+B,那么一般情况下(A+B)/M 不一定等于   A/M+B/M
AB至少有一个是M的倍数时,才可以保证N/M=A/M+B/M


2
、分步讨论
根据上面的/运算符的定义,我们可以得到问题3的初步解答,分情况讨论一下

已知N/M=X,那么当

(1)
N正好是M的倍数,即N=M*X时,那么所求的箱子数就是
X=N/M
(2)
如果N不是M的倍数,即N=M*X+Y(显然1 <=Y <M)

     
那么还要多一个箱子来装余下的Y件货物,
 则箱子总数为X+1 = N/M+1

3
、一般公式

上面的解答虽然完整,但是用起来并不方便,因为每次都要去判断NM的倍数关系,

效率不高。于是有高手怒了,就想找一个统一的公式。因此,下面的终极公式出现了,

      一个箱子最多可以装M件货物,且现有N件货物,那么所需的箱子数为

          (N+M-1)/M         (公式1)
 
这个式子用具体数字去验证是很简单的,留给读者去做。
我这里给一个完整的数学推导
:
根据/运算的定义,设N/M=X

X*M   <= N   < (X+1)*M
分情况证明一下

(1)
NM的倍数,即N=M*X时,

(N+M-1)/M

=  (M*X)/M+(M-1)/M   (分配律)

=X+0                 (用到了等式1)

=X
(2)
N不是M的倍数时,设余数为Y。则有N=M*X+Y(
1 <=Y <M),
  将不等式1 <=  Y   < M的三项同时加上M-1,就得到

      M   <=   Y-1+M   <=   2*M-1   <  2*M 

         M  <=  Y-1+M  < 2*M
        
于是根据等式2立刻得到 

        (Y-1+M) /M   =   1           

        因此,
   (N+M-1)/M  

=   (M*X+Y+M-1)/M              

=   (M*X)/M+(Y+M-1)/M               (分配律)

=   X+1                                          

     综合上面的(1)和(2),无论哪种情况,公式  (N+M-1)/M的计算结果都是正确的。
可能有的读者还会问,这个公式是怎么想出来的,怎么就想到了加上那个
M-1?
这个问题可以去看看数论中的余数理论。
 


五、掩码的作用
        
在描述本文开头的宏的确切含义之前,还得先仔细的解释一下

~(sizeof(int)-1))
的作用。


在以下的叙述中,均约定M2的幂,

M=2z=power(2,Z),    Z=log2M=log(2,M)

1、首先要介绍两个位运算的技巧:M=power(2,Z),则必有

     N/M   =  N>>Z     (公式2

     N*M   = N<<Z        (公式3

证明略。

     

2结合上面的公式2和公式3

    又可得到( N/M)*M =( N>>Z)<<Z    (公式4

    我们来解释一下( N/M)*M的结果:

    已知N/M=X,设Y=N-M*X,那么Y必然满足条件0<=Y<M,

   这里的Y就是通常整数除法中的余数,而X=N/M就是整数除法的商
   当N,M,X,Y都用二进制表示时,那么N最右边的Z位数字就是余数Y. 
剩下的左边数字就是商X。

     于是,N>>Z相当于去掉余数Y,只留下商X,即X=N/M=N>>Z

     然后(N>>Z) << Z=X<<Z=X*M,又将商扩大了M倍

     最终得到的二进制数字形式为:

     X00000..0,   ——数字的左边是商X,右边是Z个0。

     因此, ( N>>Z)<<Z的效果就是将N中的余数部分清零,商依然放在原处。

3、当M = power(2,Z)

(N >>Z) << Z = (N   &(~(M-1))     (公式5

也是一个恒等式。

证明如下:

(1) 因为M=power(2,Z),因此M的二进制形式为

1000..00,   右边共有Z0

但是在实际情况中,存储M时必须要占一个固定的位数S,左边未满的要补0,

于是当存储位数为S,M= power(2,Z)的完整存储形式是

M=0000..0010000000       

M的右边还是Z0,而左边添上了S-Z-10  

(2)根据二进制的减法,有

M-1 = 00000....01111...1,

M-1的右边有Z1,左边有S-Z

(3)根据求补运算~ 的定义,有

~(M-1)= 11111... 10000...000

~(M-1)右边有Z0,左边有S-Z

现在我们就能看出~(M-1)的效果了:它的右边Z+1位就是原来的M,而左边全是1 

(4)~(M-1)更专业的叫法就是掩码(mask)。任何一个二进制数字N和这个掩码进行&运算后,N的最右边Z位的数字全部被置0(被"掩抹"掉了).而左边保持不变。这和(N >>Z) << Z的计算结果一致。因此

(N >>Z) << Z = (N   &(~(M-1) ) 

证毕。

六、内存对齐宏的分析

 结合上面的叙述,内存对齐的问题可以归纳如下:

设一个机器字(int类型)的长度为M字节,根据实际情况,机器字的长度都是2的幂

(常见的有8位机、16位机、32位机、64位机等),

可以设M=sizeof(int)=power(2,Z),

已知某个类型的变量n的原始大小为N=sizeof(n),则在考虑内存对齐的情况下,

该变量所占用的总机器字的数目为

   ((N+M-1)/M)      (根据公式1

所以,

    n占用的总字节数=机器字数目*一个机器字所占的字节数

= ((N+M-1)/M)*M

= ((N+M-1)>>Z)<<Z   (根据公式4

=(N+M-1) & ~(M-1)     (根据公式5

= ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1)) 
最后一步就是我们看到的宏。

因此这个宏求的就是变量n在对齐情况下占用的总字节数目。

注意:
这里最关键的一点就是M必须是2的幂(有人常常理解成2的倍数也可以,那是不对的),


小结
:
1
、内存对齐的原理类似与装箱问题,其核心就是数论中的求商运算和乘法运算。首先用/运算求出占用的机器字数目(相当于箱子个数),再乘上每个机器字的长度即得到最后的总长度(相当于箱子总容量)。

2
、由于机器字的长度M都是2的幂,所以对M/运算和乘法运算可以用与运算和掩码来实现以提高效率。

 

 
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值