OpenCV源码之内存分配-指针对齐

原文:http://blog.csdn.net/lming_08/article/details/26821963?utm_source=tuicool

首先,为什么要指针对齐(Pointer Alignment)?

指针对齐有时候非常重要,因为许多硬件相关的东西在对齐上存在限制。在有些系统中,某种数据类型只能存储在偶数边界的地址处。

例如,在经典的 SPARC架构(以及经典的ARM)上,你不能从奇数地址读取一个超过1字节的整型数据。尝试这么做将会立即终止程序,并伴随着总线错误。而在X86架构上,CPU硬件处理了这个问题,只是这么做将会花费更多时间;通常RISC架构是不会为你做这些。举例如下:

  1. char c;  
  2. char *Pc = &c;  
  3. int *Pi;  
  4.   
  5. Pi = (int *)Pc;  

你可能会发现,*Pi的引用将会导致错误,因为Pc没有指向一个以"int"型边界对齐的数据项。


OpenCV2.0以上版本很多指针都是被对齐过的,使指针地址能够被16整除。OpenCV中的内存一般是通过malloc分配,不能保证申请的首地址都是都能被16整除;所以OpenCV中需要申请的内存做一些指针对齐操作。OpenCV中内存分配-指针对齐相关的函数有:

  1. void* fastMalloc( size_t size )  
  2. {  
  3.     uchar* udata = (uchar*)malloc(size + sizeof(void*) + CV_MALLOC_ALIGN);  
  4.     if(!udata)  
  5.         return OutOfMemoryError(size);  
  6.     uchar** adata = alignPtr((uchar**)udata + 1, CV_MALLOC_ALIGN);  
  7.     adata[-1] = udata;  
  8.     return adata;  
  9. }  
  10.   
  11. void fastFree(void* ptr)  
  12. {  
  13.     if(ptr)  
  14.     {  
  15.         uchar* udata = ((uchar**)ptr)[-1];  
  16.         CV_DbgAssert(udata < (uchar*)ptr &&  
  17.                ((uchar*)ptr - udata) <= (ptrdiff_t)(sizeof(void*)+CV_MALLOC_ALIGN));  
  18.         free(udata);  
  19.     }  
  20. }  

这里的CV_MALLOC_ALIGN值为16,表示实际存储数据的首地址是16的倍数;多申请的sizeof(void *)空间用来存储malloc返回的内存首地址,以便在fastFree()中可以正确释放。假设要申请的有效内存大小为x字节,即size = x;

我的系统是32位,因此sizeof(void *) = 4,下面以图解的方式讲解以上内存申请过程:

这里ptr = (uchar **)udata + 1;即是alignPtr()函数中的第一个参数,alignPtr()函数定义为:

  1. template<typename _Tp> static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_Tp))  
  2. {  
  3.     return (_Tp*)(((size_t)ptr + n-1) & -n);  
  4. }  
该函数是用来将ptr指向的地址对齐到n边界,使得到的地址是n的倍数,在这里n = CV_MALLOCD_ALIGN = 16;

ptr = 0xYY YY YY YY,那么ptr +n - 1计算如下:

  0xYY YY YY YY

+                     0F

-------------------------------

当ptr的低4位不全为0时,ptr + n - 1的结果中第5位将会进一。

然后再与(-n)相与,-n == 0xF0,所以最终返回的地址形式为0xYY YY YY Y0,即为CV_MALLOCD_ALIGN = 16的倍数。

例如,(size_t)ptr == 0或16或32 ... ...,即16的整数倍,那么返回的地址就是本身;但如果(size_t)ptr == 2,则返回的地址就是16,如果是18,则返回的地址就是32。


下面以图解的形式分别举例说明,以上分配过程。

当(size_t)ptr % CV_MALLOCD_ALIGN == 15时,

那么对齐后的地址adata相对于ptr有一个偏移量,偏移量为1;然后adata[-1] = udata,就是将malloc返回的首地址给存起来,这段空间也就是malloc时多申请的sizeof(void *)大小的空间。


当(size_t)ptr % CV_MALLOCD_ALIGN == 1时,

那么对齐后的地址adata相对于ptr有一个偏移量,偏移量为15;

fastMalloc返回的地址是adata,而由adata是很容易求出malloc返回的udata,因此fastFree(adata)中就是这样由adata求出udata,从而顺利释放内存。

采用fastMalloc()申请的内存有效利用率为


所以申请的内存块越大,其有效利用率就越大。


另外,其实在GNU系统中,由malloc或realloc返回的内存块的地址总是8的倍数(或者在64位系统上16倍);如果你需要一内存块,其地址是2的更高次幂的倍数,那么可以用stdlib.h.文件中声明的aligned_alloc 、posix_memalign


参考资料:

http://stackoverflow.com/questions/4322926/what-exactly-is-an-aligned-pointer

http://bytes.com/topic/c/answers/213142-what-pointer-alignment

http://www.cnblogs.com/summerRQ/articles/2408767.html

http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Aligned-Memory-Blocks.html

版权声明:本文为博主原创文章,未经博主允许不得转载。


猜你喜欢

转载自blog.csdn.net/woshidenghaitao/article/details/48394323