有时候为了加快大量数据的存取操作,我优化了重要路径上的算法,用尽了精简代码的技巧,还采用处理指令矢量化的技能,为程序定制了一个内存管理器以增加程序的缓冲友好性(cache friendly),一般情况下是可以达到最终目的了,可在图像处理相关的代码中,竟然发现最大优化机会在地址访问上,比如本人曾经见过这样的source code:
typedef unsigned Pixel32;
struct Pixel24 { u8 r, g, b; };
Pixel24* pRaw = new Pixel24[width*height];
Pixel32* pSrcImg = loadFromFile("tangent.bmp");
fillPort(pRaw, dstRt, pSrcImg, srcRt); // 这个函数的内部实现对于32bit pixel -> 24bit pixel比较麻烦哦
我知道,典型的IA32系的处理器的寻址操作在机器字的n倍处最快的。
C++中:
int a;
Obj x;
的代码产生的访问对象的地址由编译器完成了!而且绝对保证以最快地方式访问;
而 Obj* x;
Pixel24* pRaw = new Pixel24[sizeof(Pixel24)*width*height];
Pixel32* pSrcImg = ....
的pRaw, pSrcImg就随运行时而定,不一定在“机器字的n倍处”,而且其一部分数据在“机器字的n倍处”。
可想而知,32bit的像素在memory上很容得以地址对齐,但是也可能其首地址没有在机器字的n倍处,可24bit就更麻烦了。
所以我的策略是:动态分配时,多分配一个机器字的空间,然后想个办法让Pointer定在这个分配的线性空间内的首个“机器字的n倍处”,之后的对象访问对齐问题靠padding完成(不过,这一步只有在真的需要时才作,因为会有大量空间浪费!)。
这里给出了me的对齐code:
template
<typename Pointer, int Align =4>
Pointer alignAddress(void* raw)
{ // 把raw对齐为机器字的整数倍,Align为机器字的字节数.(需要在raw处多分配align个字节)
// Align默认为4,即32bit机的地址对齐
Pointer p = reinterpret_cast<Pointer>(
(reinterpret_cast<uintptr_t>(raw) + Align-1) & ~(Align-1) ); // 这里有点技巧
return p;
}
就这么点。
可以这样使用:
#define ALIGN_SET 4
Pixel24* pT = new unsigned char[sizeof(Pixel24)*width*height + ALIGN_SET];
Pixel24* pRaw = alignAddress<Pixel24*, ALIGN_SET>(pT);
// pRaw就是所要的地址,需要时对Pixel24开启编译器padding选项
有了这样的保证,前面的fillPort的优化工作就更容易了!