How slow are .NET exceptions?

2018-12-31 21:36发布

I don't want a discussion about when to and not to throw exceptions. I wish to resolve a simple issue. 99% of the time the argument for not throwing exceptions revolves around them being slow while the other side claims (with benchmark test) that the speed is not the issue. I've read numerous blogs, articles, and posts pertaining one side or the other. So which is it?

Some links from the answers: Skeet, Mariani, Brumme.

13条回答
低头抚发
2楼-- · 2018-12-31 22:07

I have never had any performance problem with exceptions. I use exceptions a lot -- I never use return codes if I can. They are a bad practice, and in my opinion, smell like spaghetti code.

I think it all boils down to how you use exceptions: if you use them like return codes (each method call in the stack catches and rethrows) then, yeah, they will be slow, because you have overhead each single catch/throw.

But if you throw at the bottom of the stack and catch at the top (you substitute a whole chain of return codes with one throw/catch), all costly operations are done once.

At the end of the day, they are a valid language feature.

Just to prove my point

Please run the code at this link (too big for an answer).

Results on my computer:

marco@sklivvz:~/develop/test$ mono Exceptions.exe | grep PM
10/2/2008 2:53:32 PM
10/2/2008 2:53:42 PM
10/2/2008 2:53:52 PM

Timestamps are output at the beginning, between return codes and exceptions, at the end. It takes the same time in both cases. Note that you have to compile with optimizations.

查看更多
不再属于我。
3楼-- · 2018-12-31 22:11

Just to add my own recent experience to this discussion: in line with most of what is written above, I found throwing exceptions to be extremely slow when done on a repeated basis, even without the debugger running. I just increased the performance of a large program I'm writing by 60% by changing about five lines of code: switching to a return-code model instead of throwing exceptions. Granted, the code in question was running thousands of times and potentially throwing thousands of exceptions before I changed it. So I agree with the statement above: throw exceptions when something important actually goes wrong, not as a way of controlling application flow in any "expected" situations.

查看更多
回忆,回不去的记忆
4楼-- · 2018-12-31 22:15

The argument as I understand it is not that throwing exceptions are bad they are slow per se. Instead, it is about using the throw/catch construct as a first class way of controlling normal application logic, instead of more traditional conditional constructs.

Often in normal application logic you perform looping where the same action is repeated thousands/millions of times. In this case, with some very simple profiling (see the Stopwatch class), you can see for yourself that throwing an exception instead of say a simple if statement can turn out to be substantially slower.

In fact I once read that the .NET team at Microsoft introduced the TryXXXXX methods in .NET 2.0 to many of the base FCL types specifically because customers were complaining that performance of their applications was so slow.

It turns out in many cases this was because customers were attempting type conversion of values in a loop, and each attempt failed. An conversion exception was thrown and then caught by an exception handler that then swallowed the exception and continued the loop.

Microsoft now recommend the TryXXX methods should be used particularly in this situation to avoid such possible performance issues.

I could be wrong, but it sounds like you are not certain about the veracity of the "benchmarks" you have read about. Simple solution: Try it out for yourself.

查看更多
柔情千种
5楼-- · 2018-12-31 22:18

When writing classes/functions for others to use it appears to be difficult to say when exceptions are appropriate. There are some useful parts of BCL that I had to ditch and go for pinvoke because they throw exceptions instead of returning errors. For some cases you can work around it but for others like System.Management and Performance Counters there are usages where you need to do loops in which exceptions are thrown by BCL frequently.

If you are writing a library and there's a remote possibility that your function may be used in a loop and there's a potential for large amount of iterations, use the Try.. pattern or some other way to expose the errors beside exceptions. And even then, it's hard to say how much your function will get called if it's being used by many processes in shared environment.

In my own code, exceptions are only thrown when things are so exceptional that its necessary to go look at the stack trace and see what went wrong and then fix it. So I pretty much have re-written parts of BCL to use error handling based on Try.. pattern instead of exceptions.

查看更多
若你有天会懂
6楼-- · 2018-12-31 22:20

There is the definitive answer to this from the guy who implemented them - Chris Brumme. He wrote an excellent blog article about the subject (warning - its very long)(warning2 - its very well written, if you're a techie you'll read it to the end and then have to make up your hours after work :) )

The executive summary: they are slow. They are implemented as Win32 SEH exceptions, so some will even pass the ring 0 CPU boundary! Obviously in the real world, you'll be doing a lot of other work so the odd exception will not be noticed at all, but if you use them for program flow except your app to be hammered. This is another example of the MS marketing machine doing us a disservice. I recall one microsoftie telling us how they incurred absolutely zero overhead, which is complete tosh.

Chris gives a pertinent quote:

In fact, the CLR internally uses exceptions even in the unmanaged portions of the engine. However, there is a serious long term performance problem with exceptions and this must be factored into your decision.

查看更多
千与千寻千般痛.
7楼-- · 2018-12-31 22:22

But mono throws exception 10x faster than .net standalone mode, and .net standalone mode throws exception 60x faster than .net debugger mode. (Testing machines have same CPU model)

int c = 1000000;
int s = Environment.TickCount;
for (int i = 0; i < c; i++)
{
    try { throw new Exception(); }
    catch { }
}
int d = Environment.TickCount - s;

Console.WriteLine(d + "ms / " + c + " exceptions");
查看更多
登录 后发表回答