C# 不抛出异常时,try/catch 块是否会影响性能?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1308432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do try/catch blocks hurt performance when exceptions are not thrown?
提问by Kobi
During a code review with a Microsoft employee we came across a large section of code inside a try{}
block. She and an IT representative suggested this can have effects on performance of the code. In fact, they suggested most of the code should be outside of try/catch blocks, and that only important sections should be checked. The Microsoft employee added and said an upcoming white paper warns against incorrect try/catch blocks.
在与 Microsoft 员工进行代码期间,我们在一个try{}
块中发现了一大段代码。她和一位 IT 代表建议这会对代码的性能产生影响。事实上,他们建议大部分代码应该在 try/catch 块之外,并且只应该检查重要的部分。微软员工补充说,即将发布的白皮书警告不正确的 try/catch 块。
I've looked around and found it can affect optimizations, but it seems to only apply when a variable is shared between scopes.
我环顾四周,发现它会影响优化,但它似乎只适用于在范围之间共享变量的情况。
I'm not asking about maintainability of the code, or even handling the right exceptions (the code in question needs re-factoring, no doubt). I'm also not referring to using exceptions for flow control, this is clearly wrong in most cases. Those are important issues (some are more important), but not the focus here.
我不是在问代码的可维护性,甚至不是处理正确的异常(毫无疑问,有问题的代码需要重构)。我也不是指使用异常进行流量控制,这在大多数情况下显然是错误的。这些都是重要的问题(有些更重要),但不是这里的重点。
How do try/catch blocks affect performance when exceptions are notthrown?
当不抛出异常时,try/catch 块如何影响性能?
采纳答案by Ben M
Check it.
核实。
static public void Main(string[] args)
{
Stopwatch w = new Stopwatch();
double d = 0;
w.Start();
for (int i = 0; i < 10000000; i++)
{
try
{
d = Math.Sin(1);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
w.Stop();
Console.WriteLine(w.Elapsed);
w.Reset();
w.Start();
for (int i = 0; i < 10000000; i++)
{
d = Math.Sin(1);
}
w.Stop();
Console.WriteLine(w.Elapsed);
}
Output:
输出:
00:00:00.4269033 // with try/catch
00:00:00.4260383 // without.
In milliseconds:
以毫秒为单位:
449
416
New code:
新代码:
for (int j = 0; j < 10; j++)
{
Stopwatch w = new Stopwatch();
double d = 0;
w.Start();
for (int i = 0; i < 10000000; i++)
{
try
{
d = Math.Sin(d);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
d = Math.Sin(d);
}
}
w.Stop();
Console.Write(" try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
w.Reset();
d = 0;
w.Start();
for (int i = 0; i < 10000000; i++)
{
d = Math.Sin(d);
d = Math.Sin(d);
}
w.Stop();
Console.Write("No try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
Console.WriteLine();
}
New results:
新结果:
try/catch/finally: 382
No try/catch/finally: 332
try/catch/finally: 375
No try/catch/finally: 332
try/catch/finally: 376
No try/catch/finally: 333
try/catch/finally: 375
No try/catch/finally: 330
try/catch/finally: 373
No try/catch/finally: 329
try/catch/finally: 373
No try/catch/finally: 330
try/catch/finally: 373
No try/catch/finally: 352
try/catch/finally: 374
No try/catch/finally: 331
try/catch/finally: 380
No try/catch/finally: 329
try/catch/finally: 374
No try/catch/finally: 334
回答by arul
Quite comprehensive explanation of the .NET exception model.
Rico Mariani's Performance Tidbits: Exception Cost: When to throw and when not to
Rico Mariani 的表演花絮:异常成本:何时抛出,何时不抛出
The first kind of cost is the static cost of having exception handling in your code at all. Managed exceptions actually do comparatively well here, by which I mean the static cost can be much lower than say in C++. Why is this? Well, static cost is really incurred in two kinds of places: First, the actual sites of try/finally/catch/throw where there's code for those constructs. Second, in unmanged code, there's the stealth cost associated with keeping track of all the objects that must be destructed in the event that an exception is thrown. There's a considerable amount of cleanup logic that must be present and the sneaky part is that even code that doesn't itself throw or catch or otherwise have any overt use of exceptions still bears the burden of knowing how to clean up after itself.
第一种成本是在代码中进行异常处理的静态成本。托管异常在这里实际上做得相对较好,我的意思是静态成本可以比 C++ 中说的低得多。为什么是这样?好吧,静态成本确实在两种地方产生:首先,try/finally/catch/throw 的实际站点,那里有这些构造的代码。其次,在无人管理的代码中,存在与跟踪所有在抛出异常时必须销毁的对象相关的隐蔽成本。必须存在大量的清理逻辑,而狡猾的部分是即使代码没有
Dmitriy Zaslavskiy:
德米特里·扎斯拉夫斯基:
As per Chris Brumme's note: There is also a cost related to the fact the some optimization are not being performed by JIT in the presence of catch
根据 Chris Brumme 的说明:还有一个成本与在 catch 存在的情况下 JIT 没有执行某些优化这一事实有关
回答by John Kugelman
No. If the trivial optimizations a try/finally block precludes actually have a measurable impact on your program, you probably should not be using .NET in the first place.
不。如果 try/finally 块排除的微不足道的优化实际上对您的程序产生了可衡量的影响,那么您可能首先不应该使用 .NET。
回答by Guffa
I tested the actual impact of a try..catch
in a tight loop, and it's too small by itself to be a performance concern in any normal situation.
我try..catch
在一个紧密的循环中测试了 a 的实际影响,它本身太小,在任何正常情况下都不会成为性能问题。
If the loop does very little work (in my test I did an x++
), you can measure the impact of the exception handling. The loop with exception handling took about ten times longer to run.
如果循环做的工作很少(在我的测试中我做了一个x++
),您可以衡量异常处理的影响。带有异常处理的循环的运行时间大约是该循环的 10 倍。
If the loop does some actual work (in my test I called the Int32.Parse method), the exception handling has too little impact to be measurable. I got a much bigger difference by swapping the order of the loops...
如果循环做了一些实际工作(在我的测试中我调用了 Int32.Parse 方法),异常处理的影响太小而无法衡量。通过交换循环的顺序,我得到了更大的差异......
回答by RHicke
try catch blocks have a negligible impact on performance but exception Throwing can be pretty sizable, this is probably where your coworker was confused.
try catch 块对性能的影响可以忽略不计,但异常抛出可能相当大,这可能是您的同事感到困惑的地方。
回答by awe
The structure is different in the example from Ben M. It will be extended overhead inside the inner for
loop that will cause it to not be good comparison between the two cases.
示例中的结构与Ben M 不同。它将在内for
循环内部扩展开销,这将导致它无法很好地比较两种情况。
The following is more accurate for comparison where the entire code to check (including variable declaration) is inside the Try/Catch block:
以下是更准确的比较,其中要检查的整个代码(包括变量声明)在 Try/Catch 块内:
for (int j = 0; j < 10; j++)
{
Stopwatch w = new Stopwatch();
w.Start();
try {
double d1 = 0;
for (int i = 0; i < 10000000; i++) {
d1 = Math.Sin(d1);
d1 = Math.Sin(d1);
}
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
finally {
//d1 = Math.Sin(d1);
}
w.Stop();
Console.Write(" try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
w.Reset();
w.Start();
double d2 = 0;
for (int i = 0; i < 10000000; i++) {
d2 = Math.Sin(d2);
d2 = Math.Sin(d2);
}
w.Stop();
Console.Write("No try/catch/finally: ");
Console.WriteLine(w.ElapsedMilliseconds);
Console.WriteLine();
}
When I ran the original test code from Ben M, I noticed a difference both in Debug and Releas configuration.
当我从Ben M运行原始测试代码时,我注意到 Debug 和 Releas 配置的不同。
This version, I noticed a difference in the debug version (actually more than the other version), but it was no difference in the Release version.
这个版本,我注意到debug版本有区别(其实比其他版本多),但是Release版本没有区别。
Conclution:
Based on these test, I think we can say that Try/Catch doeshave a small impact on performance.
结论:
基于这些测试,我认为我们可以说 Try/Catch确实对性能的影响很小。
EDIT:
I tried to increase the loop value from 10000000 to 1000000000, and ran again in Release to get some differences in the release, and the result was this:
编辑:
我尝试将循环值从 10000000 增加到 1000000000,然后在 Release 中再次运行以获取版本中的一些差异,结果是这样的:
try/catch/finally: 509
No try/catch/finally: 486
try/catch/finally: 479
No try/catch/finally: 511
try/catch/finally: 475
No try/catch/finally: 477
try/catch/finally: 477
No try/catch/finally: 475
try/catch/finally: 475
No try/catch/finally: 476
try/catch/finally: 477
No try/catch/finally: 474
try/catch/finally: 475
No try/catch/finally: 475
try/catch/finally: 476
No try/catch/finally: 476
try/catch/finally: 475
No try/catch/finally: 476
try/catch/finally: 475
No try/catch/finally: 474
You see that the result is inconsequent. In some cases the version using Try/Catch is actually faster!
你会看到结果是无关紧要的。在某些情况下,使用 Try/Catch 的版本实际上更快!
回答by Isaac
The try/catch HAS impact on the performance.
try/catch 对性能有影响。
But its not a huge impact. try/catch complexity is generally O(1), just like a simple assignment, except when they are placed in a loop. So you have to use them wisely.
但影响不大。try/catch 的复杂度通常是 O(1),就像一个简单的赋值,除非它们被放置在一个循环中。所以你必须明智地使用它们。
Hereis a reference about try/catch performance (doesn't explain the complexity of it though, but it is implied). Take a look at Throw Fewer Exceptionssection
这是一个关于 try/catch 性能的参考(虽然没有解释它的复杂性,但它是隐含的)。看看抛出更少的异常部分
回答by TheVillageIdiot
After seeing all the stats for with try/catch and without try/catch, curiosity forced me to look behindto see what is generated for both the cases. Here is the code:
看到所有的统计信息与try / catch语句,没有的try / catch后,好奇迫使我看后面,看两者的情况下产生的。这是代码:
C#:
C#:
private static void TestWithoutTryCatch(){
Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1));
}
MSIL:
MSIL:
.method private hidebysig static void TestWithoutTryCatch() cil managed
{
// Code size 32 (0x20)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "SIN(1) = {0} - No Try/Catch"
IL_0006: ldc.r8 1.
IL_000f: call float64 [mscorlib]System.Math::Sin(float64)
IL_0014: box [mscorlib]System.Double
IL_0019: call void [mscorlib]System.Console::WriteLine(string,
object)
IL_001e: nop
IL_001f: ret
} // end of method Program::TestWithoutTryCatch
C#:
C#:
private static void TestWithTryCatch(){
try{
Console.WriteLine("SIN(1) = {0}", Math.Sin(1));
}
catch (Exception ex){
Console.WriteLine(ex);
}
}
MSIL:
MSIL:
.method private hidebysig static void TestWithTryCatch() cil managed
{
// Code size 49 (0x31)
.maxstack 2
.locals init ([0] class [mscorlib]System.Exception ex)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldstr "SIN(1) = {0}"
IL_0007: ldc.r8 1.
IL_0010: call float64 [mscorlib]System.Math::Sin(float64)
IL_0015: box [mscorlib]System.Double
IL_001a: call void [mscorlib]System.Console::WriteLine(string,
object)
IL_001f: nop
IL_0020: nop
IL_0021: leave.s IL_002f //JUMP IF NO EXCEPTION
} // end .try
catch [mscorlib]System.Exception
{
IL_0023: stloc.0
IL_0024: nop
IL_0025: ldloc.0
IL_0026: call void [mscorlib]System.Console::WriteLine(object)
IL_002b: nop
IL_002c: nop
IL_002d: leave.s IL_002f
} // end handler
IL_002f: nop
IL_0030: ret
} // end of method Program::TestWithTryCatch
I'm not an expert in IL but we can see that an local exception object is created on fourth line .locals init ([0] class [mscorlib]System.Exception ex)
after that things are pretty same as for method without try/catch till the line seventeen IL_0021: leave.s IL_002f
. If an exception occurs the control jumps to line IL_0025: ldloc.0
otherwise we jump to label IL_002d: leave.s IL_002f
and function returns.
我不是 IL 方面的专家,但我们可以看到在第四行创建了一个本地异常对象,.locals init ([0] class [mscorlib]System.Exception ex)
之后的事情与没有 try/catch 的方法非常相同,直到第 17 行IL_0021: leave.s IL_002f
。如果发生异常,控制跳转到 lineIL_0025: ldloc.0
否则我们跳转到 labelIL_002d: leave.s IL_002f
并且函数返回。
I can safely assume that if no exceptions occur then it is the overhead of creating local variables to hold exception objects onlyand a jump instruction.
我可以安全地假设,如果没有发生异常,那么它是创建局部变量以仅保存异常对象和跳转指令的开销。
回答by Ira Baxter
See discussion on try/catch implementationfor a discussion of how try/catch blocks work, and how some implementations have high overhead, and some have zero overhead, when no exceptions occur. In particular, I think the Windows 32 bit implementation has high overhead, and the 64 bit implementation does not.
请参阅有关 try/catch 实现的讨论,以了解有关 try/catch 块如何工作的讨论,以及在没有异常发生时,某些实现如何具有高开销,而某些实现具有零开销。特别是,我认为Windows 32位实现有很高的开销,而64位实现则没有。
回答by supercat
In theory, a try/catch block will have no effect on code behavior unless an exception actually occurs. There are some rare circumstances, however, where the existence of a try/catch block may have a major effect, and some uncommon-but-hardly-obscure ones where the effect can be noticeable. The reason for this is that given code like:
理论上,除非真正发生异常,否则 try/catch 块不会对代码行为产生影响。然而,在一些罕见的情况下,try/catch 块的存在可能会产生重大影响,而在一些不常见但几乎不模糊的情况下,这种影响可能会很明显。这样做的原因是给定的代码如下:
Action q;
double thing1()
{ double total; for (int i=0; i<1000000; i++) total+=1.0/i; return total;}
double thing2()
{ q=null; return 1.0;}
...
x=thing1(); // statement1
x=thing2(x); // statement2
doSomething(x); // statement3
the compiler may be able to optimize statement1 based upon the fact that statement2 is guaranteed to execute before statement3. If the compiler can recognize that thing1 has no side-effects and thing2 doesn't actually use x, it may safely omit thing1 altogether. If [as in this case] thing1 was expensive, that could be a major optimization, though the cases where thing1 is expensive are also those the compiler would be least likely to optimize out. Suppose the code were changed:
编译器可能能够基于语句 2 保证在语句 3 之前执行的事实来优化语句 1。如果编译器可以识别出thing1 没有副作用并且thing2 实际上没有使用x,那么它可以安全地完全省略thing1。如果 [在这种情况下] thing1 很昂贵,那可能是一个主要的优化,尽管 thing1 很昂贵的情况也是编译器最不可能优化的情况。假设代码已更改:
x=thing1(); // statement1
try
{ x=thing2(x); } // statement2
catch { q(); }
doSomething(x); // statement3
Now there exists a sequence of events where statement3 could execute without statement2 having executed. Even if nothing in the code for thing2
could throw an exception, it would be possible that another thread could use an Interlocked.CompareExchange
to notice that q
was cleared and set it to Thread.ResetAbort
, and then perform a Thread.Abort()
before statement2 wrote its value to x
. Then the catch
would execute Thread.ResetAbort()
[via delegate q
], allowing execution to continue with statement3. Such a sequence of events would of course be exceptionally improbable, but a compiler is required to generate code which work according to specification even when such improbable events occur.
现在存在一系列事件,其中 statement3 可以在没有执行 statement2 的情况下执行。即使代码中没有任何内容thing2
可以抛出异常,另一个线程也有可能使用 anInterlocked.CompareExchange
来通知q
已清除并将其设置为Thread.ResetAbort
,然后执行Thread.Abort()
before statement2 将其值写入x
。然后catch
将执行Thread.ResetAbort()
[via delegate q
],允许继续执行 statement3。这样的事件序列当然是异常不可能的,但是需要编译器生成即使在发生这种不可能的事件时也能根据规范工作的代码。
In general, the compiler is much more likely to notice opportunities to leave out simple bits of code than complex ones, and thus it would be rare for a try/catch could affect performance much if exceptions are never thrown. Still, there are some situations where the existence of a try/catch block may prevent optimizations which--but for the try/catch--would have allowed code to run faster.
一般来说,编译器更有可能注意到忽略简单代码位的机会,而不是复杂的代码位,因此如果从不抛出异常,try/catch 很少会影响性能。尽管如此,在某些情况下,try/catch 块的存在可能会阻止优化——但对于 try/catch——本来可以让代码运行得更快。