2008年十一月月 发布的文章

这张梅西的照片我最喜欢


这幅图片是梅西2008-09赛季对阵维尔瓦打入世界波进球后的庆祝场面,图片中梅西的姿态很舒展,面目很清晰,在新浪体育的评论栏中居然有网友说这里的梅西像哈利波特^_^。另外2008-09赛季巴萨的队服我也甚是喜欢。不知道真品巴萨队服(M10的)是不是很昂贵呢,以前没有关注过。

注:图片来源于新浪体育

四谈内存对齐

最近在思考改进项目中一模块的实现,该模块维护起来让我很是头疼,所有才有了整体换掉它的想法。设计和实现中利用了内存对齐的技术。关于内存对齐,我曾经写过三篇文章,第一篇介绍了计算内存对齐的方法和例子,第二篇说了一个内存对齐的应用;三谈内存对齐时,则从其本质上做了阐述,而这次实际上是继续在其本质上的做文章,结合本质谈谈为什么内存对齐的计算方法就应该是第一篇中所讲的那两条。

如果对内存对齐本质还不清楚的话,就看看我的内存对齐系列的第三篇吧。如果你清楚了本质,那么我们结合第一篇中交待的内存对齐计算方法来进一步探究,为什么计算的方法就是这个样子的。

再理解一下对齐系数/模数,众所周知Alignment module反映了CPU 取数据时对数据起始地址的限制-这个地址值必须能被Alignment module所整除,但继续仔细考虑下去,你会想到CPU在下一次取数据依然要从下一个能被Alignment module所整除的地址的地方去取,这显而易见,又能说明什么呢?如果说CPU第一次取数据的地址是first_read_address,那么连续下一次的地址就应该是first_read_address + Alignment module了,也就是说每次取数据的量就是Alignment module这么多,这样通过Alignment module我们又可以知道一个量,那就是:在Alignement module限制下,CPU一次能取Alignment module个字节;在“Data alignment: Straighten up and fly right”一文中作者也称之为"memory access granularity"。从应用层开发人员的角度理解:被访问变量的长度,就是CPU要去读取的字节数;而对齐系数又是限制CPU读取字节数的一个指标,有了这两个理解,解决下面的疑问就有了基础了。

在'也谈内存对齐'一篇中介绍了内存对齐的计算方法,这里不妨再引用一下:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

疑问:对于数据成员对齐规则:为什么每个成员的对齐都要按照Min(指定对齐模数,数据成员自身长度)来确定呢?为了不用Max(指定对齐模数,数据成员自身长度)呢,用Max值对齐的不更加完美么?同样对于结构的整体对齐规则也一样有此疑问。这里我们还是举个例子更加直观:
#pragma pack(8)
struct Foo {
    char    a;
    int    b;
    short    c;
};
#pragma pack()
我们先来看数据成员对齐,以b为例子,按照规则的说法,sizeof(b) = 4 < 8,那么Address_of_b = Start_Address_of_Foo + 4; 我们来看看当应用的代码里访问b的时候,CPU做了些什么?Address_of_b一半情况下是不能被8整除的,在不能被8整除的情况下,我们去访问b,这里我要提两个问题:
1) 访问b的时候是否会因内存没有对齐到8上而触发core呢?(在Sun SPARC上因访问未对齐地址上的变量时会出core)
2) 为什么不将b放到Start_Address_of_Foo + 8这个地址上呢?

下面一一说说我的理解:
根据前面所说,程序在访问b的时候,CPU实际不一定是从Address_of_b这个地址上开始读取的。如果b这个地址恰巧既能被4整除,也能被8整除(如地址24),那就无可厚非了。但是如果这个地址只能被4整除,而不能被8整除(如地址12),那么此时CPU读取的地址肯定是从Address_of_b – 4开始读取8个字节的,也就是说实际上CPU都是从能被8整除的地址上读取的,而且一次读了8个字节,b所在的位置恰是这个8个字节中的后4个字节,所以不存在触发core的可能。

第二个问题,sizeof(b) = 4 < 8,为什么就要按照4而不是按照8去安排b的地址呢?我们不妨按照8去给b分配地址,Address_of_b' = Start_Address_of_Foo + 8,这样的话CPU也能一次将b读取,而且是从b的起始地址开始读,似乎更完美。但你看出问题了么?这么做浪费的空间显然大了很多。将b安排在Address_of_b'比安排在Address_of_b多浪费了一半空间。

同样整体对齐原则也是同样的道理。内存对齐计算显然有两个目标:一是减少CPU的访存次数;第二个就是还要保持存储空间的效率足够高。

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats