如C++般飞奔的C#

用C#实现一个图像边缘检测算法,处理一个300*375的图片竟然用了2.38s,吐血。用VSTS的代码分析工具发现,GDI+里面的GetPixel()这个函数竟然占用了运行时间的53.92%而SetPixel()占用了7.14%,光是图像的读入和输出就占了60%+(见图1)。这样的速度可受不了。稍微分析一下就能得出原因,Bitmap是一个通用类,可以用来处理多种图像格式,因此GetPixe()l和SetPixel()的效率自然会在诸多分支跳转中受到拖累,再加上这两个函数被频繁反复调用,对程序效率的影响尤为明显。再深入想一想,实际上这样的函数无非是把一块内存复制并返回而已,因此可以考虑直接深入Bitmap的内部用指针糟吊搞,这样不仅可以省去反复调用函数压栈弹栈内存分配等琐碎工作,也能够在图像格式已经确定下来的前提下节约分支跳转的时间。



图1 VSTS中对代码运行的采样分析结果

放狗搜了一下,Google大神告诉我们,其实微软已经考虑到了这样的效率问题,提供了两个东西,一个叫做BitmapData类,专门给需要效率的同学实现上面的思路,一个是传说中的Unsafe Code支持,使得C#中仍然可以使用指针,为BitmapData的使用创造了条件。至于细节上如何使用这个类,线程安全性,内存如何对齐等问题,详询10086,此处不述。或者也可以拜读这位同学的文章:使用C#进行图像处理的几种方法。(此文赞一个,写得相当清楚)
采用这种方案对实现进行改进后,对同一张图片的处理时间变为了0.19s,时间缩短了92%,这还是可以接受的。当然,进一步的改进仍然需要,这就是另一个问题了。

由此可见,当效率不理想的时候,不要怨天尤人,不要怪C#或Java此类语言先天不足。而应该先用工具搞清楚问题究竟出在什么地方,然后抓主要矛盾,着重优化这几个函数。此外,有的放狗也是重要技巧之一。一开始用关键字"GDI+ 效率 C#",搜出来一堆关于"GDI+比OpenGL效率还高"的资料,后来换了"GetPixel C# 效率",就搜出了相关文章。此外在没有网络的时候也可以去看看MSDN,事实上如果认真阅读MSDN中Bitmap.Members这个文档的话,会发现LockBits()这个函数,深入探究下去也可以找到相当详细的资料和例程。总而言之,积极的态度,理性的分析和工具的合理利用是克服困难的有力武器。

posted on 2010-01-25 22:14  grapeot  阅读(767)  评论(0编辑  收藏  举报

导航