mouser0316(大飞工作室) https://passport2.21ic.com/?679346 [收藏] [复制] [RSS] 我们服务宗旨是:以诚信为本,顾客为上,为企业和个人提供价廉物美、物超所值的服务。大飞工作室将为您提供以下服务:单片机、DSP、FPGA等各种电子产品设计及图像处理软件开发。联系QQ:5608 5195,11208 06651 手机:1343715 1115,138 6677 1024

日志

巧用栈/片内存储器来加快图像运算

已有 761 次阅读2011-1-24 12:18 |系统分类:视频音频| 图像, 加速

图像处理的很多算法中N*N的运算复杂度经常给程序运行效率带来掣肘,并且由于效率的不足,使得我们的图像算法减少了在很多场合下的适用性。


算法编写、测试完成后,多数情况下,我们会尝试采用很多措施来提高程序运行效率,包括算法代码优化和编译选项的优化。例如Intel平台下,我们启用SSE2指令集以利用处理器扩展的运算方式来提高运行效率。


 


使用该增强指令集,需要处理器的支持,并且如果希望有较好的优化效果,需要了解SSE2的优化原理并针对算法代码进行一些修改。进一步的知识可以参考很多优秀的网文,这里只是举出例子,说明可以利用处理器自身的能力进行优化。同样的,在DSP中,例如ADI BlackFin系列,同样可以通过外部存储器映射等等方法来提高处理器读取/运算效率。


通过以上的方式进行代码优化后,很多时候,效率依然是差强人意的,并且由于需要掌握更多的核心架构知识,实际的优化过程操作起来比较复杂,简单的更改编译选项却又是不能够满足需求的。


这里介绍一种原理比较简单,但很多时候能带来效率飞跃的方法:使用效率更高的存储区来进行运算数据的存取,会使运算效率大大提高。


在上位机代码中,将本来存放在堆(Heap)上的数据,存放到栈(stack)上。其对运算效率的提升预期可以通过反汇编代码中的指令条数变化来获得。


 


可以看到堆上数据的写操作汇编指令(红色框中)是2条,而栈上(绿色框中)则减少成了1条。也就是说更换到栈上来操作,效率可以提升1倍!具体的编译原理和程序数据区划分,有兴趣的朋友可以进一步去补充相关的知识,这里不做赘述。


然而堆是程序中最灵活的内存分配方式,而栈的大小却受到了很大的限制。一般的图像处理程序,单张图片的存储需求都是很大的,试图在栈上分配过大的数组空间,无可避免的会stack overflow。以下就解决这个问题给出两个简单实用的方法:


1、改变栈大小设置


如下图,当图片大小不是很过分,同时程序其他栈数据分配较小时,可以尝试采用更改编译选项中的堆栈大小的方法来给栈进行一定的扩容,解决stackoverflow。(图例中设置了16MByte的栈大小)


 


当然,如果对临时栈数据的分配(某个函数中的临时变量数组)不那么放心的话,建议还是在调用该函数时捕捉一下stackoverflow的异常:
 _try
 {
   SomeProcThatHeavilyUseStack();
 }
 _except(EXCEPTION_EXECUTE_HANDLER)
 {
  if (_exception_code()==0xC00000FD)
  {
     ExceptionHandlerProcedure();  // stack overflow 异常处理
  }
 }


2、利用高效的memcpy把栈作为运算时的数据缓冲


我们都有这样的经验,那就是通过for{for{}}来把某个数据区内的数据完全赋值给另一个数据区,这样做的效率是极其低下的,而通过memcpy则可以数十倍的提高拷贝效率。


那么在栈的大小不够用时,我们可以通过先将存储在堆上的数据中的一个部分拷贝出来


memcpy(stackSrcPtr,heapSrcPtr,len);


进行相关运算后,再将结果拷贝出来


memcpy(stackDestPtr,heapDestPtr,len);


当然这种用法,需要对算法进行一些改动,这些改动通常是在for..for之间进行的,把1行行的数据运算完毕后,再拷出结果。我们把这种作为数据缓冲的栈的大小称为运算帧。


这种方法有一定的局限性,就是当图像运算空间相关半径超过了能接受的stack size时,或者是运算的空间相关性很复杂时,这种方法将无法或很难应用。所以,通过堆栈数据对倒来利用栈存取速度快的方法需要视情况来选用。


 


以上方法只是应用在了上位机代码中,当使用DSP等平台进行图像处理运算时,我们只有片内存储器和片外存储器之分。由于片内存储器一般较小,大量的图像数据是存储在外部存储器中的(如SDRAM等)。处理器通过外部接口访问数据,其速度会大大下降,尤其是Image[somePosition]这种访问。


其实,可以通过巧妙的变形,并套用上面介绍的方法二来提升数据存取效率。


即,与其需要某个像素数据时再去取,不如利用DMA,在不占用内核时间的情况下,将下一个运算帧的数据拷贝到片内存储器上。


基本的过程可以归纳为:(算法开始准备)打开DMA,传输一运算帧数据->(DMA完毕中断到来)开启下一帧DMA->进行当前运算帧数据的运算->等待中断并循环进行{DMA->运算过程}直至全部数据运算完毕->算法结束。


无疑,这种应用方式较为复杂,但是对于苦于片内存储器太小,而片外存储器访问太慢的应用来说,确是一种打破瓶颈的好方法。这种方法,在运算帧DMA传输时间小于运算帧算法时间时,会使对外部存储器的访问效果几乎等同于片内存储。


路过

鸡蛋

鲜花

握手

雷人

全部作者的其他最新日志

评论 (0 个评论)