您的位置:首页 > 其它

0.033秒的艺术 --- 测试程序性能

2009-05-17 23:11 218 查看
这是一系列关于C#与游戏编程性能提示的文章,先来看看如何简单测试一段C#程序的性能。

0.033秒的艺术 --- 测试程序性能

仅供个人学习使用,请勿转载,勿用于任何商业用途。

交互式的实时3D程序每秒至少渲染30帧图像,才能保证视觉上的平滑,这意味着所有渲染,AI,物理仿真都必须在0.033秒内完成。只有高度优化的代码和算法才能达到这样的要求。如何判断某个算法是否能满足要求,如何比较哪一种算法才是最优的呢?方法有很多,通过丢硬币,撒骰子或者凭感觉猜测等等,不过这些方法通常需要一点点运气的帮助。可惜我不是每天都能有那么好的运气,所以测试是我通常选择的方法。永远不要在测试结束之前作出任何结论。

几乎所有的测试都以时间和空间代价为主要衡量标准。然而考虑到目前2G内存已经降到了130米以下的价格,所以这里我只关心时间。但是也不要忘了程序和数据所占的空间越小,越能获得缓存所带来的好处,提高命中率。如何来测量一段程序的执行时间呢?啊,你可能马上想到了System.Times和System.Windows.Forms名称空间下的Timer或许还有DateTime。可惜这些计时器的精度都不太理想,通常几十毫秒才更新一次。我们需要高精度的时钟,这将用到2个win32 API:

QueryPerformanceCounter() 这个函数返回硬件支持的高精度计时器的值。

QueryPerformanceFrequency() 返回硬件计时器的频率。

通过直接访问硬件,这是一个精度非常高的时钟,可以把它的返回值想象为CPU的晶震数和晶振频率。在测试开始和结束的时候,分别查询一次计时器的值,最后相减除以计时器的频率,就能得到精确的时间间隔,以秒为单位,当然,可以进一步转换为任何你觉得方便的单位。可以说目前绝大部分3D游戏的计时系统都是以这2个函数为基础。为了在C#下使用他们,需要通过P/Invoke导入这这两个函数。好了,现在可以这样来测试一段程序的性能:

class TestClass

{

static void Main(String[] args)

{

Stopwatch timer = new Stopwatch();

// initialize your code here

GC.Collect(2);

GC.WaitForPendingFinalizers();

GC.Collect(2);

GC.WaitForPendingFinalizers();

timer.Start();

//Do your test here

timer.Stop();

Console.WriteLine(timer.ElapsedMilliseconds.ToString());

}

}

测试之前强制垃圾回收。WaitForPendingFinalizers()将会挂起当前线程,直到GC完全结束。注意,这里强制进行了2次垃圾回收。如果你对垃圾回收有所了解的话,那么应该知道当GC发现一个对象有Finalize()方法时,会先调用Finalize(),然后到下一次GC发生时才回收这个对象,我们确保在测试之前所有垃圾都已经回收释放了。

好了,现在我们有了一个简单实用的测试模板,不过还有几点忠告是在测试时必须记住的 :

1. 在测试结束之前永远不要下结论

2. 在测试之前,至少执行一次测试代码。比如你的测试代码是Test(),那么在timer.start()前至少执行一次Test(),排除JIT对测试结果的影响。

3. 多运行几次程序,取平均值作为测试结果

4. 不要在Debug模式下执行测试

5. 不要在测试代码中输出测试信息,Console是相对较慢的操作。

6. 不同的CLR和编译器版本下的测试结果可能会不同。

以上忠告都来自好心的Tobias Hertkorm

现在你已经成为性能分析大师了。不要再到论坛上问究竟是for还是foreach快这样的问题了,自己动手试试吧。当然,如果你比我还懒,那么下次来看我对for和foreach的分析结果吧 :)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: