Pla*_*ure 71 .net c# performance try-finally
我们已经看到很多关于何时以及为何使用try
/ catch
和try
/ catch
/的问题finally
.我知道try
/ 肯定有一个用例finally
(特别是因为它是using
语句实现的方式).
我们还看到了有关try/catch和异常开销的问题.
然而,我所链接的问题并没有谈到JUST try-finally的开销.
假设try
块中发生的任何事情都没有异常,那么确保finally
语句在离开try
块时执行的开销是多少(有时是从函数返回)?
再一次,我只询问try
/ finally
,不catch
,不抛出异常.
谢谢!
编辑:好的,我将尝试更好地展示我的用例.
我应该使用哪个,DoWithTryFinally
或者DoWithoutTryFinally
?
public bool DoWithTryFinally()
{
this.IsBusy = true;
try
{
if (DoLongCheckThatWillNotThrowException())
{
this.DebugLogSuccess();
return true;
}
else
{
this.ErrorLogFailure();
return false;
}
}
finally
{
this.IsBusy = false;
}
}
public bool DoWithoutTryFinally()
{
this.IsBusy = true;
if (DoLongCheckThatWillNotThrowException())
{
this.DebugLogSuccess();
this.IsBusy = false;
return true;
}
else
{
this.ErrorLogFailure();
this.IsBusy = false;
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
这种情况过于简单,因为只有两个返回点,但想象一下是否有四个......或十个......或一百个.
在某些时候,我想使用try
/ finally
出于以下原因:
this.Working
设置为false
.所以假设,考虑到性能问题,可维护性和DRY原则,对于多少个退出点(特别是如果我可以假设所有内部异常都被捕获),我是否希望产生与try
/ 相关的任何性能损失finally
?
编辑#2:我改名this.Working
为this.IsBusy
.对不起,忘记提到这是多线程的(虽然只有一个线程实际上会调用该方法); 其他线程将轮询以查看对象是否正在执行其工作.如果工作按预期进行,则返回值仅仅是成功或失败.
pli*_*nth 95
为什么不看看你到底得到了什么?
这是C#中的一小段代码:
static void Main(string[] args)
{
int i = 0;
try
{
i = 1;
Console.WriteLine(i);
return;
}
finally
{
Console.WriteLine("finally.");
}
}
Run Code Online (Sandbox Code Playgroud)
以下是调试版本中生成的IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init ([0] int32 i)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: nop
L_0004: ldc.i4.1
L_0005: stloc.0
L_0006: ldloc.0 // here's the WriteLine of i
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: leave.s L_001d // this is the flavor of branch that triggers finally
L_000f: nop
L_0010: ldstr "finally."
L_0015: call void [mscorlib]System.Console::WriteLine(string)
L_001a: nop
L_001b: nop
L_001c: endfinally
L_001d: nop
L_001e: ret
.try L_0003 to L_000f finally handler L_000f to L_001d
}
Run Code Online (Sandbox Code Playgroud)
这是在调试中运行时由JIT生成的程序集:
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[00288D34h],0
00000028 je 0000002F
0000002a call 59439E21
0000002f xor edx,edx
00000031 mov dword ptr [ebp-40h],edx
00000034 nop
int i = 0;
00000035 xor edx,edx
00000037 mov dword ptr [ebp-40h],edx
try
{
0000003a nop
i = 1;
0000003b mov dword ptr [ebp-40h],1
Console.WriteLine(i);
00000042 mov ecx,dword ptr [ebp-40h]
00000045 call 58DB2EA0
0000004a nop
return;
0000004b nop
0000004c mov dword ptr [ebp-20h],0
00000053 mov dword ptr [ebp-1Ch],0FCh
0000005a push 4E1584h
0000005f jmp 00000061
}
finally
{
00000061 nop
Console.WriteLine("finally.");
00000062 mov ecx,dword ptr ds:[036E2088h]
00000068 call 58DB2DB4
0000006d nop
}
0000006e nop
0000006f pop eax
00000070 jmp eax
00000072 nop
}
00000073 nop
00000074 lea esp,[ebp-0Ch]
00000077 pop ebx
00000078 pop esi
00000079 pop edi
0000007a pop ebp
0000007b ret
0000007c mov dword ptr [ebp-1Ch],0
00000083 jmp 00000072
Run Code Online (Sandbox Code Playgroud)
现在,如果我注释掉try和finally以及return,我会从JIT获得几乎相同的程序集.您将看到的差异是跳转到finally块和一些代码以确定在执行finally之后要去哪里.所以你在谈论TINY的差异.在发布中,跳转到finally将被优化掉 - 大括号是nop指令,所以这将成为跳转到下一条指令,这也是一个nop - 这是一个简单的窥视孔优化.pop eax和jmp eax同样便宜.
{
00000000 push ebp
00000001 mov ebp,esp
00000003 push edi
00000004 push esi
00000005 push ebx
00000006 sub esp,34h
00000009 mov esi,ecx
0000000b lea edi,[ebp-38h]
0000000e mov ecx,0Bh
00000013 xor eax,eax
00000015 rep stos dword ptr es:[edi]
00000017 mov ecx,esi
00000019 xor eax,eax
0000001b mov dword ptr [ebp-1Ch],eax
0000001e mov dword ptr [ebp-3Ch],ecx
00000021 cmp dword ptr ds:[00198D34h],0
00000028 je 0000002F
0000002a call 59549E21
0000002f xor edx,edx
00000031 mov dword ptr [ebp-40h],edx
00000034 nop
int i = 0;
00000035 xor edx,edx
00000037 mov dword ptr [ebp-40h],edx
//try
//{
i = 1;
0000003a mov dword ptr [ebp-40h],1
Console.WriteLine(i);
00000041 mov ecx,dword ptr [ebp-40h]
00000044 call 58EC2EA0
00000049 nop
// return;
//}
//finally
//{
Console.WriteLine("finally.");
0000004a mov ecx,dword ptr ds:[034C2088h]
00000050 call 58EC2DB4
00000055 nop
//}
}
00000056 nop
00000057 lea esp,[ebp-0Ch]
0000005a pop ebx
0000005b pop esi
0000005c pop edi
0000005d pop ebp
0000005e ret
Run Code Online (Sandbox Code Playgroud)
因此,您正在谈论尝试/最终的非常非常小的成本.很重要的问题领域很少.如果你正在做类似memcpy的事情,并在每个被复制的字节周围尝试/最后,然后继续复制数百MB的数据,我可以看到这是一个问题,但在大多数情况下?可以忽略不计.
Bri*_*sen 53
所以我们假设有一个开销.你打算停止使用finally
吗?希望不是.
IMO绩效指标仅在您可以选择不同选项时才相关.我无法看到你如何在finally
不使用的情况下获得语义finally
.
And*_*ber 28
try/finally
非常轻巧.实际上,try/catch/finally
只要没有抛出异常,也是如此.
我有一个快速的配置文件应用程序,我前一段时间测试它; 在一个紧凑的循环中,它确实没有添加任何执行时间.
我会再发一次,但这很简单; 只是运行一个紧凑的循环做一些事情,一个try/catch/finally
不会在循环中抛出任何异常,并将结果与没有try/catch/finally
.
Nic*_*sen 11
我们实际上为此设置了一些基准数字.这个基准测试显示的是,实际上,尝试/最终的时间与调用空函数的开销一样小(可能更好地说:"跳转到下一条指令",正如IL专家所说的那样)以上).
static void RunTryFinallyTest()
{
int cnt = 10000000;
Console.WriteLine(TryFinallyBenchmarker(cnt, false));
Console.WriteLine(TryFinallyBenchmarker(cnt, false));
Console.WriteLine(TryFinallyBenchmarker(cnt, false));
Console.WriteLine(TryFinallyBenchmarker(cnt, false));
Console.WriteLine(TryFinallyBenchmarker(cnt, false));
Console.WriteLine(TryFinallyBenchmarker(cnt, true));
Console.WriteLine(TryFinallyBenchmarker(cnt, true));
Console.WriteLine(TryFinallyBenchmarker(cnt, true));
Console.WriteLine(TryFinallyBenchmarker(cnt, true));
Console.WriteLine(TryFinallyBenchmarker(cnt, true));
Console.ReadKey();
}
static double TryFinallyBenchmarker(int count, bool useTryFinally)
{
int over1 = count + 1;
int over2 = count + 2;
if (!useTryFinally)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
// do something so optimization doesn't ignore whole loop.
if (i == over1) throw new Exception();
if (i == over2) throw new Exception();
}
return sw.Elapsed.TotalMilliseconds;
}
else
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; i++)
{
// do same things, just second in the finally, make sure finally is
// actually doing something and not optimized out
try
{
if (i == over1) throw new Exception();
} finally
{
if (i == over2) throw new Exception();
}
}
return sw.Elapsed.TotalMilliseconds;
}
}
Run Code Online (Sandbox Code Playgroud)
结果:33,33,32,35,32 63,64,69,66,66(毫秒,确保您有代码优化)
因此,1000万次循环中的try/finally大约需要33毫秒的开销.
每次尝试/最后,我们正在谈论0.033/10000000 =
try/finally的3.3纳秒或33亿分之一秒的开销.
归档时间: |
|
查看次数: |
5276 次 |
最近记录: |