C# 中 try/finally 的开销?
我们已经看到很多关于何时以及为何使用 try
/catch
和 try
/catch
/< 的问题代码>最后。我知道 try
/finally
肯定有一个用例(特别是因为它是 using
语句的实现方式)。
我们还看到了有关 try/ 开销的问题捕获和异常。
然而,我所链接的问题并没有讨论“JUST try-finally”的开销。
假设 try
块中发生的任何事情都没有异常,那么确保在离开 try
时执行 finally
语句的开销是多少> 阻止(有时通过从函数返回)?
再说一次,我只询问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;
}
}
这种情况过于简单化,因为只有两个返回点,但想象一下如果有四个……或十个……或一百个。
在某些时候,我想使用 try
/finally
,原因如下:
- 保持 DRY 原则(特别是当退出点的数量变得更高时)
- 如果事实证明我的内部函数没有抛出异常是错误的,然后我想确保
this.Working
设置为false
。
因此,假设考虑到性能问题、可维护性和 DRY 原则,我想要多少个退出点(特别是如果我可以假设所有内部异常都被捕获)会产生与 try
/finally
相关的性能损失吗?
编辑#2:我将this.Working
的名称更改为this.IsBusy
。抱歉,忘记提及这是多线程的(尽管只有一个线程实际上会调用该方法);其他线程将轮询以查看该对象是否正在执行其工作。返回值仅仅是工作是否按预期进行的成功或失败。
We've seen plenty of questions about when and why to use try
/catch
and try
/catch
/finally
. And I know there's definitely a use case for try
/finally
(especially since it is the way the using
statement is implemented).
We've also seen questions about the overhead of try/catch and exceptions.
The question I linked to, however, doesn't talk about the overhead of having JUST try-finally.
Assuming there are no exceptions from anything that happens in the try
block, what's the overhead of making sure that the finally
statements get executed on leaving the try
block (sometimes by returning from the function)?
Again, I'm asking ONLY about try
/finally
, no catch
, no throwing of exceptions.
Thanks!
EDIT: Okay, I'm going to try to show my use case a little better.
Which should I use, DoWithTryFinally
or 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;
}
}
This case is overly simplistic because there are only two return points, but imagine if there were four... or ten... or a hundred.
At some point I would want to use try
/finally
for the following reasons:
- Keep to DRY principles (especially as the number of exit points gets higher)
- If it turns out that I'm wrong about my inner function not throwing an exception, then I want to make sure
this.Working
is set tofalse
.
So hypothetically, given performance concerns, maintainability, and DRY principles, for what number of exit points (especially if I can assume that all inner exceptions are caught) do I want to incur whatever performance penalty is associated with try
/finally
?
EDIT #2: I changed the name of this.Working
to this.IsBusy
. Sorry, forgot to mention this is multithreaded (though only one thread will ever actually call the method); other threads will be polling to see if the object is doing its work. The return value is merely success or failure for if the work went as expected.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
为什么不看看你实际得到了什么?
这是 C# 中的一段简单代码:
这是调试构建中生成的 IL:
这是在调试中运行时 JIT 生成的程序集:
现在,如果我注释掉 try、finally 和 return,我会得到来自 JIT 的几乎相同的组装。您将看到的差异是跳转到finally 块以及一些代码来确定finally 执行后该去哪里。所以你说的是微小的差异。在发布中,跳转到finally将得到优化 - 大括号是nop指令,所以这将成为跳转到下一条指令,这也是一个nop - 这是一个简单的窥视孔优化。 pop eax 和 jmp eax 同样便宜。
所以你说的是 try/finally 的成本非常非常小。很少有问题领域对此很重要。如果您正在执行类似 memcpy 的操作,并在复制的每个字节周围进行尝试/最终操作,然后继续复制数百 MB 的数据,我可以看到这是一个问题,但在大多数情况下?微不足道。
Why not look at what you actually get?
Here is a simple chunk of code in C#:
And here is the resulting IL in the debug build:
and here's the assembly generated by the JIT when running in debug:
Now, if I comment out the try and finally and the return, I get nearly identical assembly from the JIT. The differences you'll see are a jump into the finally block and some code to figure out where to go after the finally is executed. So you're talking about TINY differences. In release, the jump into the finally will get optimized out - braces are nop instructions, so this would become a jump to the next instruction, which is also a nop - that's an easy peephole optimization. The pop eax and then jmp eax is similarly cheap.
So you're talking very, very tiny costs for try/finally. There are very few problem domains where this matters. If you're doing something like memcpy and put a try/finally around each byte being copied and then proceed to copy hundreds of MB of data, I could see that being an issue, but in most usage? Negligible.
因此,我们假设存在开销。那么你打算停止使用
finally
吗?但愿不会。IMO 性能指标仅在您可以在不同选项之间进行选择时才相关。我不明白如何在不使用
finally
的情况下获得finally
的语义。So let's assume there's an overhead. Are you going to stop using
finally
then? Hopefully not.IMO performance metrics are only relevant if you can choose between different options. I cannot see how you can get the semantic of
finally
without usingfinally
.try/finally
非常轻量级。其实,只要不抛出异常,try/catch/finally
也是如此。我不久前做了一个快速配置文件应用程序来测试它;在一个紧密的循环中,它实际上没有增加任何执行时间。
我会再次发布它,但它非常简单;只需运行一个紧密循环来执行某些操作,并使用不会在循环内引发任何异常的
try/catch/finally
,并根据没有try/catch/finally< 的版本对结果进行计时/代码>。
try/finally
is very lightweight. Actually, so istry/catch/finally
as long as no exception is thrown.I had a quick profile app I did a while ago to test it out; in a tight loop, it really added nothing at all to execution time.
I'd post it again, but it was really simple; just run a tight loop doing something, with a
try/catch/finally
that does not throw any exceptions inside the loop, and time the results against a version without thetry/catch/finally
.让我们实际上为此添加一些基准数字。这个基准测试表明,事实上,尝试/最终的时间大约与调用空函数的开销一样小(可能更好的说法是:“跳转到下一条指令”,正如 IL 专家所说的那样)多于)。
结果:33,33,32,35,32 63,64,69,66,66
(毫秒,请确保您已启用代码优化)
因此,在 1000 万 循环中,try/finally 的开销约为 33 毫秒。
那么,每次 try/finally 的开销为 0.033/10000000 =
3.3 纳秒,即 33 亿分之一秒的 try/finally 开销。
Let's actually put some benchmark numbers to this. What this benchmark shows is that, indeed, the time of having a try/finally is about as small as the overhead of a call to an empty function (probably better put: "a jump to the next instruction" as the IL expert stated it above).
Result: 33,33,32,35,32 63,64,69,66,66
(milliseconds, make sure you have code optimization on)
So about 33 milliseconds overhead for the try/finally in 10 million loops.
Per try/finally then, we are talking 0.033/10000000 =
3.3 nanoseconds or 3.3 billionth of a second overhead of a try/finally.
安德鲁·巴伯说的话。除非抛出异常,否则实际的 TRY/CATCH 语句不会增加或可以忽略不计的开销。最后没什么特别的。在 try+catch 语句中的代码完成后,您的代码总是跳转到finally
What Andrew Barber said. The actual TRY/CATCH statements add no/negligible overhead unless an exception is thrown. There's nothing really special about finally. Your code just always jumps to finally after the code in the try+catch statements are done
在较低级别中,如果不满足条件,
finally
与else
一样昂贵。它实际上是汇编程序(IL)中的跳转。In lower levels
finally
is just as expensive as anelse
if the condition not met. It is actually a jump in assembler (IL).