Warning: Cannot modify header information - headers already sent by (output started at /home4/yusendai/public_html/liujiong/blog/wp-config.php:1) in /home4/yusendai/public_html/liujiong/blog/wp-includes/feed-rss2.php on line 8
Sharpmark | 刘炯的博客 » simple http://liujiong.com Version 3.0 Wed, 21 Oct 2009 17:38:15 +0000 http://wordpress.org/?v=2.9.1 en hourly 1 数据对齐 http://liujiong.com/blog/posts/align-data/ http://liujiong.com/blog/posts/align-data/#comments Mon, 12 Feb 2007 07:55:00 +0000 sharpmark http://218.240.14.21/~sharpmark/?p=79 看《编程卓越之道Write Great Code》的时候,在看数据总线的时候,明白了一些内存访问的基本原理,而最近在做SIMPLE,正好可以利用内存访问的一个技巧来优化一下我的程序。这 个技巧就是”数据对齐”。顺便拿出来分享一下。我会简单说一下数据对齐的由来,原因和应用方法。

首先说数据对其这种说法怎么来的。我以使 用16位(16-bit)数据总线的处理器来举例。32位的原理相同。处理器将16位的数据总线分为两部分d0-d7为低字节,d8-d15为高字节。相 应将内存分为对应两列。偶地址的接在d0-d7数据线上,奇地址的接在d8-d15位上。这样处理器就可以任意访问一个偶地址的数据,或一个奇地址的数 据,或者一个以偶地址开头的字(Word, 1word == 2byte)。
然而,例如在80×86系列的16位处理器上,它允许从任意地址读 取一个字。处理器从指定的地址取得低端字节,而从下一个地址获得高端字节。如果低端地址是偶地址的话,没有什么问题。但是,如果是奇地址的话,那么数据的 高端字节就需要到下一个字去获得了。例如希望从地址21获取一个字。这个数据位置就是在21, 22两个字节。但是要注意,处理只能将偶地址作为低位放入地址总线,所以处理器首先通过访问地址为20, 21的字节,获得一个字,然后再访问下一个字,获得地址为22, 23的两个字节。并在内部将地址21和地址22的数据交换位置,通过数据总线传递给处理器。
这在大规模访问连续数据的时候就会产生问题(当然其他 时候也可能会有)。例如,一个图像数据顺序保存在内存中,假设每个象素是两字节,数据的起始字节是奇数。并假设处理器没有做任何优化,没有缓存,那么每个 象素的读取都会有两次的内存访问,一次的高低字节数据交换。一个800*600的图像就会有480000次的多余内存访问。这个效率会差很多。所以要想办 法解决。

这里我介绍两种需要手动设置数据的对齐的情况。
1. 在一个大的数据块的开始,如果数据没有对齐,那么后面的数据都回相应错位。设置数据开始部分对齐的方法如下:(从SIMPLE中摘录,为了便于阅读,有改 动,另由于blogger对尖括号的支持不够好,所以下面的代码中可能使用全角的尖括号代替半角尖括号,使用时请自行替换。)

[code:c]< T* > alignPointer(void * raw, int align)
{
T*p = reinterpret_cast<T*>(
(reinterpret_cast<uintptr_t>(raw) + align - 1)
&~(align - 1);
return p;
}[/code]

解释一下,假设我们需要N个字节的数据,并以align对齐。(align==0,1的时候不对齐, 2的时候两字节对齐,以此类推,align为2的幂。)。void * raw是申请的空间,大小为N + align – 1个空间。多余的align – 1个空间是为了对齐而预留的。T类型是指定的每个元素的类型。

首先,通过reinterpret_cast<uintptr_t>(raw)将raw转化成uintptr_t类型,(uintptr_t是一种足够大从而可以保存指针的类型,如果没有定义uintptr_t的系统,可以使用typedef int uintptr_t来自行定义)。
结果加align – 1,相当于这个元素最后一个字节的位置。然后对~(align – 1)取与(and)运算。相当与计算出元素最后一个字节所在的内存的对齐列的第一个字节的地址。这么说有些晕,我来举个例子:例如分配n个字节,其中起始地址是7,对齐字节是4字节对齐。如果不对齐的话那么这个数据的第一个元素占用的就是7~10字节。如果对齐的话,首先7 + (4 – 1) = 10,计算出理论的最后一个字节的地址是10。然后10 & ~3。换算成二进制更好理解:1011 & 1100 = 1000,也就是十进制的8。所以起始的字节是从8开始的,那么地址是7的字节以及分配的最后两个字节会被忽略。再假设起始地址是4,那么计算后还是4,不用对齐。
2. 行对齐。在图像的内存表示中,除了开头的内存对其外,还有一个地方需要注意,就是行与行之间的对齐。例如一个RGB像素3字节,一个2*3的图像,再内存中的布局应该是:

0- 2为(0, 0) 3- 5为(1, 0) 6- 7空闲
8-10为(0, 1) 11-13为(1, 1) 14-15空闲
16-18为(0, 2) 19-21为(1, 2) 22-23空闲

图像的每一行都需要8个字节存储。不过其中仅有6个字节包含象素数据。前三个字节保存该行第一个象素的存储,其后3个字节保存下一个像素。为了另下一行从双字开始,在存储下一行的象素之前必须跳过两个字节。这里的做法跟上面的那种差不多,所以就不再举出代码了。

]]>
http://liujiong.com/blog/posts/align-data/feed/ 0