嗯,我觉得你安排时间的方式很糟糕。只安排整个循环的时间会更明智:
var秒表=秒表。开始新建();对于(int i=1;i<100000000;i++){斐博(100);}秒表。停止();慰问。WriteLine(“已用时间:{0}”,秒表。已用);
这样,您就不会受微小计时、浮点运算和累积错误的支配。
进行了更改后,看看“非捕获”版本是否仍比“捕获”版本慢。
编辑:好的,我自己也试过了,我也看到了同样的结果。非常奇怪。我想知道try/catch是否禁用了一些糟糕的内联,但使用了[方法实现(MethodImplOptions.NoInlining)]
反而无济于事。。。
我想,基本上你需要在cordbg下查看优化的JITted代码。。。
编辑:更多信息:
- 尝试/捕捉
n++;
行仍然可以提高性能,但不如将其放在整个块上
- 如果捕捉到特定异常(
参数异常
在我的测试中)它仍然很快
- 如果在catch块中打印异常,它仍然很快
- 如果在catch块中重新抛出异常,那么速度又会变慢
- 如果使用finally块而不是catch块,那么速度又会变慢
- 如果使用finally块以及拦网,很快
奇怪的。。。
编辑:好的,我们有拆卸。。。
这是使用C#2编译器和。NET2(32位)CLR,使用mdbg反汇编(因为我的机器上没有cordbg)。即使在调试器下,我仍然可以看到相同的性能效果。快速版本使用尝试
用一个捕获{}
处理程序。显然,除了没有try/catch之外,慢速版本是相同的。调用代码(即Main)在这两种情况下都是相同的,并且具有相同的程序集表示形式(因此这不是内联问题)。
快速版本的反汇编代码:
[0000]按ebp[0001]移动ebp,尤指[0003]推动edi[0004]推动esi[0005]推送ebx[0006]子esp,1Ch[0009]x或eax,eax[000b]mov双字ptr[ebp-20h],eax[000e]mov双字ptr[ebp-1Ch],eax[0011]mov双字ptr[ebp-18h],eax[0014]mov双字ptr[ebp-14h],eax[0017]x或eax,eax[0019]mov双字ptr[ebp-18h],eax*[001c]移动esi,1[0021]异或edi,edi[0023]mov双字ptr[ebp-28h],1[002a]mov双字ptr[ebp-24h],0[0031]包括ecx[0032]移动ebx,2[0037]cmp-ecx,2号[003a]吉利00000024[003c]移动eax,esi[003e]移动编辑,编辑[0040]移动esi,双字ptr[ebp-28h][0043]mov edi,双字ptr[ebp-24h][0046]添加eax,双字ptr[ebp-28h][0049]adc edx,双字ptr[ebp-24h][004c]mov双字ptr[ebp-28h],eax[004f]mov双字ptr[ebp-24h],edx[0052]包括ebx[0053]cmp-ebx、ecx[0055]jl法国队E7[0057]jmp 0000000 7[0059]致电64571ACB[005e]mov eax,双字ptr[ebp-28h][0061]mov edx,双字ptr[ebp-24h][0064]lea-esp,[ebp-0Ch][0067]流行ebx[0068]流行esi[0069]流行编辑[006a]弹出ebp[006b]转
慢速版本的反汇编代码:
[0000]按ebp[0001]移动ebp,尤指[0003]推动esi[0004]子esp,18小时*[0007]mov双字ptr[ebp-14h],1[000e]mov双字ptr[ebp-10h],0[0015]mov双字ptr[ebp-1Ch],1[001c]mov双字ptr[ebp-18h],0[0023]包括ecx[0024]移动esi,2[0029]cmp-ecx,2号[002c]吉利00000031[002e]mov eax,双字ptr[ebp-14h][0031]mov edx,双字ptr[ebp-10h][0034]mov双字ptr[ebp-0Ch],eax[0037]mov双字ptr[ebp-8],edx[003a]mov eax,双字ptr[ebp-1Ch][003d]移动edx,双字ptr[ebp-18h][0040]mov双字ptr[ebp-14h],eax[0043]mov双字ptr[ebp-10h],edx[0046]mov eax,双字ptr[ebp-0Ch][0049]mov edx,双字ptr[ebp-8][004c]添加eax,dword ptr[ebp-1Ch][004f]adc edx,双字ptr[ebp-18h][0052]mov双字ptr[ebp-1Ch],eax[0055]mov双字ptr[ebp-18h],edx[0058]包括[0059]cmp-esi,ecx公司[005b]jl FFFF D3型[005d]mov eax,双字ptr[ebp-1Ch][0060]mov edx,双字ptr[ebp-18h][0063]学习esp,[ebp-4][0066]流行esi[0067]弹出ebp[0068]转
在每种情况下*
显示调试器在简单的“单步执行”中输入的位置。
编辑:好的,我现在已经看过代码了,我想我可以看到每个版本的工作原理。。。我认为较慢的版本较慢,因为它使用更少的寄存器和更多的堆栈空间。对于较小的值n个
这可能更快,但当循环占用大部分时间时,速度会变慢。
可能是try/catch块军队需要保存和恢复更多寄存器,因此JIT也将这些寄存器用于循环。。。这正好提高了整体性能。目前尚不清楚JIT是否合理决定不在“正常”代码中使用尽可能多的寄存器。
编辑:刚在我的x64机器上试过。x64 CLR是许多的在这段代码中,它比x86 CLR快(大约快3-4倍),在x64下,try/catch块没有明显的区别。