装配中的脑袋

用程序装配大脑,再用大脑装配程序
随笔 - 118, 评论 - 1214, 引用 - 11

导航

关于

如果想发较大的信件,请用Ninputer @ gmail.com

不要在我的Blog评论中张贴广告,除非同意向我付款。

标签

每月存档

最新留言

广告

 

.NET Framework 2.0中增加了一项新的诊断工具——“停表”StopWatch类,相比以前的计时系统,停表采用的是高精度计时方式,因此对性能测量更有帮助。为了试用StopWatch类,我选择了一个经典的测试题目,VB中的IIf函数。IIf函数与C系列的?:三元运算符十分相似,可以在一行语句内完成判断和赋值两项任务。但是,IIf是函数而不是运算符,其接受Object类型参数这一项已经够让人担忧的了。IIf到底有多慢呢?我们比较三个版本。首先是VB类库中的Object版:

Public Function IIf(Expression As Boolean, _
    TruePart As Object, FalsePart As Object)As Object

第二个版本,是我编写的泛型版本的IIf,这个版本可以用于减少装箱拆箱过程带来的性能损失:

Public Function IIf(Of T)(Expression As Boolean, _
    TruePart As T, FalsePart As T)As T
    If Expression Then
        Return TruePart
    Else
        Return FalsePart
    End If
End Function

第三个版本当然是直接使用If语句。我们已经很清楚哪个比较快,但是没有一个量的概念,通过高精度计时的测试,可以让我看到函数内联化带来的收益是否够多。下面是计时和测试的代码:

Dim watch As New System.Diagnostics.Stopwatch

Dim j As Integer = 0
Dim f As Boolean = True

'Loop 10 times and get the average time
For times As Integer = 1 To 10

    watch.Start()
    For i As Integer = 0 To 1000000
        j = Interaction.IIf(f, i, 5)
    Next
    watch.Stop()

Next

GC.Collect()

TextBox1.AppendText("Object IIf: " & watch.ElapsedMilliseconds / 10 & vbCrLf)

watch.Reset()
For times As Integer = 1 To 10

    watch.Start()
    For i As Integer = 0 To 1000000
        j = IIf(f, i, 5)
    Next
    watch.Stop()

Next

GC.Collect()

TextBox1.AppendText("Generic IIf: " & watch.ElapsedMilliseconds / 10 & vbCrLf)

watch.Reset()
For times As Integer = 1 To 10

    watch.Start()
    For i As Integer = 0 To 1000000
        If f Then j = i Else j = 5
    Next
    watch.Stop()

Next

TextBox1.AppendText("Pure If: " & watch.ElapsedMilliseconds / 10 & vbCrLf)

我们看到StopWatch可以像真的停表一样开始,停止或继续,因此我们可以像在科学实验中计时一样使用它。而他的Elapsed属性可以提供各种单位的时间间隔。简单起见,我就用了毫秒。循环的次数不太多,取10次平均。最后的结果在我的机器上如下:

Object IIf: 219.6
Generic IIf: 51.5
Pure If: 9.7

纯If仅9.7毫秒的间隔也能测试出来,足见StopWatch的精度比原来的计时手段高多了。而结果也表明,函数调用的代价已经相当可观,但还不如装箱、拆箱带来的代价高。IIf当然是不要再用了,今后自己写函数时也应当注意,频繁调用的函数不能不考虑调用代价,内联化是不错的解决方案。

打印 | 张贴于 2004-07-31 13:29:00 | Tag:技术随笔

留言反馈

#回复: 试用“停表”高精度计时 编辑
我是一位教师,想要一个停表动画,做一个看一下,那就好呀
2007-09-04 17:59:00 | [匿名:周忠红]
#回复: 试用“停表”高精度计时 编辑
做一个看一下嘛
2007-09-04 17:57:00 | [匿名:周忠红]
#re: 试用“停表”高精度计时 编辑
Dim watch As New System.Diagnostics.Stopwatch
我装了FRAMWORK 2.0可是编译的时候却说 类未定义
2004-12-22 00:38:00 | [匿名:YEJY]
#re: 试用“停表”高精度计时 编辑
问一个无关的话题,为什么代码里面要用GC.Collect()?

如果有其他的app在跑,这三处调用,可能会对server的性能造成很大影响的吧?
2004-08-04 17:11:00 | [匿名:鞠强]
#re: 试用“停表”高精度计时 编辑
好像有说参数是值类型就不会内联化,也不知道T算不算值类型
2004-08-03 14:19:00 | [匿名:Ninputer]
#re: 试用“停表”高精度计时 编辑
另外不妨试一下用下面的代码测试一下,看看到底用的是Debug还是Release:

#if DEBUG
MessageBox.Show("Debug");
#else
MessageBox.Show("Release");
#endif
2004-08-03 11:21:00 | [匿名:sumtec]
#re: 试用“停表”高精度计时 编辑
@Ninputer:
如果是A的话,那么就应该有机会内联化。因为一切都是可推测的,固定的。实际上内联化的过程不是在编译阶段的时候进行的,而是在JIT阶段进行的,甚至应该在两到三次的JIT之后进行的。还是觉得内联化是有可能的事情。
2004-08-03 11:20:00 | [匿名:sumtec]
#re: 试用“停表”高精度计时 编辑
@sumtec

会是A,泛型不会对编译期间任何不清楚的东西加以推测。如果你给两个参数分别传递A类型和B类型的变量,就会出错,会要求用Of显式指定类型参数。
2004-08-02 13:46:00 | [匿名:Ninputer]
#re: 试用“停表”高精度计时 编辑
@Ninputer:
为什么不会内联化呢?我是说这个说法有没有根据?我没有看到有说泛型函数不会内联化的说法,当然我也没有排除这个可能性。但是从我的想象来说,应该是可以内联的,因为虽然你的函数本身没有办法确定T的类型,但是调用方却可以确定使用的是哪一个类型的函数。不过这个我也不能够确定……

顺带请教你一个问题:如果有
class A{}
class B:A{}
A x = new B();
那么
IIf(x,...)这样的调用,你说实际应该是T = A 还是 T = B呢?
2004-08-02 13:04:00 | [匿名:sumtec]
#re: 试用“停表”高精度计时 编辑
@sumtec

我用了VB Profile,那Debug/Releas的选项就不知道跑哪里去了:(,不过我是热身10遍才测试的。至于自动内联化,我觉得泛型函数是不会内联化的,所以我才用它而不是直接用Integer类型的IIf。
2004-08-02 12:41:00 | [匿名:Ninputer]
#re: 试用“停表”高精度计时 编辑
@Ninputer:

我在想,不知道你这里是不是用了Debug版本进行测试的?我在想,如果用Release的话,可能你的那个IIf(Of T)的函数应该会被内联化的,看起来这个函数的IL不会超过32个,也没有复杂的控制转移。另外,可能你直接这么测回把JIT时间也测出来了。如果可以的话,调用函数之前最好先热身大约十次,然后再测试,这样可能会比较准确一点。
2004-08-02 10:55:00 | [匿名:sumtec]
#re: 试用“停表”高精度计时 编辑
@Ninputer:

是啊,确实方便很多。有空研究一下它内部的实现机制吧,也许用的就是PerformanceCounter呢。:)
2004-08-01 14:03:00 | [匿名:JGTM'2004 [MVP]]
#re: 试用“停表”高精度计时 编辑
@JGTM'2004 [MVP]

以前当然要用那个,但是每次都要声明API,我经常想不起来那些函数的名称。现在有了StopWatch,才可以做到“想侧就测”
2004-08-01 09:37:00 | [匿名:Ninputer]
#re: 试用“停表”高精度计时 编辑
现在有了StopWatch, 性能问题大家都可以经常测试,以决定最佳方案。
2004-07-31 20:58:00 | [匿名:Ninputer]
#re: 试用“停表”高精度计时 编辑
在程序中尽量少用函数调用,原来如此的重要~切记
2004-07-31 19:48:00 | [匿名:Sky.Cheung]
#re: 试用“停表”高精度计时 编辑
box/unbox的消耗也太大了吧,比函数调用还大好多倍!
2004-07-31 14:12:00 | [匿名:cnlamar(无中生有)]
#re: 试用“停表”高精度计时 编辑
一直知道裝箱、拆箱開銷挺大的。
沒想到這麼大呀。
2004-07-31 14:07:00 | [匿名:lichdr]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.2.0