Compact Framework下的测试
厄,实际上这是一份和上面的代码很类似的C#代码:
... Bitmap backBuffer = new Bitmap(BOX_WIDTH, BOX_HEIGHT); Brush boxBrush;Brush ellipseBrush = new SolidBrush(Color.Green); Graphics screenGraphics = f.CreateGraphics(); // Graphics.FromHdc(screenDC); Graphics backGraphics = Graphics.FromImage(backBuffer); for (i = 0; i < GDI_ITERATIONS_PER_REPORT; i++) { width += xop; height += yop; if ((width >= BOX_WIDTH) || (width <= 0)) xop *= -1; if ((height >= BOX_HEIGHT) || (height <= 0)) yop *= -1; r += rop; g += gop; b += bop; if ((r > 254) || (r < 1)) rop *= -1; if ((g > 254) || (g < 1)) gop *= -1; if ((b > 254) || (b < 1)) bop *= -1; boxBrush = new SolidBrush(Color.FromArgb(r, g, b)); backGraphics.FillRectangle(boxBrush, 0, 0, BOX_WIDTH, BOX_HEIGHT); boxBrush.Dispose(); backGraphics.FillEllipse(ellipseBrush, width, height, BOX_WIDTH - 2 * width, BOX_HEIGHT - 2 * height); backGraphics.FillEllipse(ellipseBrush, height, width, BOX_WIDTH - 2 * height, BOX_HEIGHT - 2 * width); screenGraphics.DrawImage(backBuffer, 0, 0); } |
桌面PC机上的测试
通常来说,托管代码和本地代码的效率应该5%左右,充其量有10%的差别,而以上结果(15%~20%)确实有些让人感到意外。考虑到有可能是.NET Compact Framework和我们通常用的桌面版本的Framework有所不同,接下来我们再来做一个桌面PC机上的测试。我们稍稍改动了一下代码,加了几条预编译指令,让之前的代码托管代码能在PC机上运行。结果是托管的程序循环速度为825次每秒,本地代码的程序循环速度则达到了6200次每秒。这比托管的程序快了625%!!
改进后的托管代码
为什么托管代码会如此之慢,设想那些托管方法如Graphics.FillRectangle只是通过P/invoke对Win32API FillRect的简单封装。那何不直接P/invoke来调用本地GDI代码呢,这样可以减少方法调用的次数。
改进后测试结果表明性能有所提高,在模拟器和Axim上分别达到了455和225次每秒,较之前提高了7%。这将托管代码和本地代码的差别缩小了一半。再回到桌面上,测试结果让人更加吃惊,托管代码也达到了每秒6150次循环。和之前的本地代码只差了1%。看来,托管代码并不总是比本地代码慢那么多。
总结
既然托管代码或多或少的要比非托管代码慢,那么是不是使用费托管代码就一定优越一些呢?我觉得并非如此,二者各有各的优势,各有各的适应场合。
考虑到托管代码快速开发的特性,日益强大的IDE和工具支持,托管代码无疑是经典企业级应用的不二选择。使用托管代码意味着以更低的成本更少的风险更快地占领市场。你会不喜欢吗?
类似的,对于非托管代码,有些情况,你只能使用它来开发,比如一个以太网卡驱动程序。但是一般的应用是否一定要用费托管代码,这就是值得商榷的了,就像刚刚那个例子,即使非托管代码的确很快,就算它能达到每秒30帧的刷新速度,但是问题是,真的需要那么快么?对人的眼睛来说,也许根本不用那么高的速度。这就是说,不同的应用还是得具体分析。如果你打算做一个模拟飞行的游戏,你的程序大多是跟绘图有关的,那么显然,这时候应当选择使用非托管代码。但是如果你只打算做一个纸牌游戏,或者一个文本编辑器呢?所以,纯粹的性能并不是决定你选择使用哪种方式开发的唯一因素。主要还是要看你怎么分析具体的需求,然后选择一条格式的途径
完整代在这里下载
关注此文的读者还看过: