C# .NET 异常有多慢?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/161942/
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
How slow are .NET exceptions?
提问by Goran
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?
我不想讨论何时抛出异常和不抛出异常。我想解决一个简单的问题。99% 的情况下,不抛出异常的论点都围绕着异常缓慢而另一方声称(通过基准测试)速度不是问题。我已经阅读了大量有关某一方面的博客、文章和帖子。那么它是哪个?
采纳答案by Jon Skeet
I'm on the "not slow" side - or more precisely "not slow enough to make it worth avoiding them in normal use". I've written two shortarticlesabout this. There are criticisms of the benchmark aspect, which are mostly down to "in real life there'd be more stack to go through, so you'd blow the cache etc" - but using error codes to work your way up the stack would alsoblow the cache, so I don't see that as a particularly good argument.
我站在“不慢”的一边——或者更准确地说“不够慢,值得在正常使用中避免它们”。我写了两篇关于这个的短文。有对基准方面的批评,主要归结为“在现实生活中会有更多的堆栈要通过,所以你会炸毁缓存等”——但使用错误代码在堆栈上工作也会破坏缓存,所以我不认为这是一个特别好的论点。
Just to make it clear - I don't support using exceptions where they're not logical. For instance, int.TryParse
is entirely appropriate for converting data from a user. It's appropriate when reading a machine-generated file, where failure means "The file isn't in the format it's meant to be, I really don't want to try to handle this as I don't know what else might be wrong."
只是为了说清楚 - 我不支持在不合逻辑的情况下使用异常。例如,int.TryParse
完全适用于转换来自用户的数据。在读取机器生成的文件时这是合适的,其中失败意味着“文件不是它想要的格式,我真的不想尝试处理这个,因为我不知道还有什么可能是错误的。 ”
When using exceptions in "only reasonable circumstances" I've never seen an application whose performance was significantly impaired by exceptions. Basically, exceptions shouldn't happen often unless you've got significant correctness issues, and if you've got significant correctness issues then performance isn't the biggest problem you face.
在“仅在合理的情况下”使用异常时,我从未见过性能受到异常显着影响的应用程序。基本上,除非您有重大的正确性问题,否则不应经常发生异常,如果您有重大的正确性问题,那么性能并不是您面临的最大问题。
回答by leppie
In release mode the overhead is minimal.
在发布模式下,开销最小。
Unless you are going to be using exceptions for flow-control (example, non-local exits) in a recursive fashion, I doubt you will be able to notice the difference.
除非您打算以递归方式使用异常进行流控制(例如,非本地退出),否则我怀疑您是否能够注意到其中的差异。
回答by Ash
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.
我所理解的论点并不是抛出异常是不好的,它们本身很慢。相反,它是关于使用 throw/catch 构造作为控制正常应用程序逻辑的第一类方法,而不是更传统的条件构造。
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.
通常在正常的应用程序逻辑中,您执行循环,其中相同的操作重复数千/数百万次。在这种情况下,通过一些非常简单的分析(参见 Stopwatch 类),您可以亲眼看到抛出异常而不是简单的 if 语句会显着变慢。
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.
事实上,我曾经读到 Microsoft 的 .NET 团队在 .NET 2.0 中向许多基本 FCL 类型引入了 TryXXXXX 方法,特别是因为客户抱怨他们的应用程序的性能太慢。
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.
Microsoft 现在建议应在这种情况下特别使用 TryXXX 方法以避免此类可能的性能问题。
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.
我可能是错的,但听起来您不确定您所阅读的“基准”的真实性。简单的解决方案:自己尝试一下。
回答by Sklivvz
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.
但是如果你在堆栈的底部抛出并在顶部捕获(你用一个 throw/catch 替换整个返回码链),所有昂贵的操作都完成一次。
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
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.
时间戳在开头输出,在返回代码和异常之间,在结尾输出。这两种情况都需要相同的时间。请注意,您必须进行优化编译。
回答by Quibblesome
If you compare them to return codes they are slow as hell. However as previous posters stated you don't want to throw in normal program operation so you only get the perf hit when a problem occurs and in the vast majority of cases performance no longer matters (as the exception implies a road-block anyway).
如果将它们与返回代码进行比较,它们会慢得要命。然而,正如之前的海报所说,您不想投入正常的程序操作,因此您只会在出现问题时获得性能命中,并且在绝大多数情况下,性能不再重要(因为异常意味着无论如何都会遇到障碍)。
They're definately worth using over error codes, the advantages are vast IMO.
它们绝对值得在错误代码上使用,优势是巨大的 IMO。
回答by gbjbaanb
There is the definitive answer to this from the guy who implemented them - Chris Brumme. He wrote an excellent blog articleabout 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 :) )
实施它们的人 - Chris Brumme 给出了明确的答案。他写了一篇关于这个主题的优秀博客文章(警告 - 很长)(警告 2 - 写得很好,如果你是技术人员,你会读到最后,然后下班后必须补上你的时间:) )
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 expect 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.
执行摘要:它们很慢。它们被实现为 Win32 SEH 异常,所以有些甚至会通过 ring 0 CPU 边界!显然,在现实世界中,您将做很多其他工作,因此根本不会注意到奇怪的异常,但是如果您将它们用于程序流程,则希望您的应用程序受到打击。这是 MS 营销机器伤害我们的另一个例子。我记得一位 microsoftie 告诉我们他们是如何产生绝对零开销的,这完全是废话。
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.
事实上,即使在引擎的非托管部分,CLR 在内部也使用异常。但是,例外情况存在严重的长期性能问题,您的决定中必须考虑到这一点。
回答by Cory Foy
I have no idea what people are talking about when they say they are slow only if they are thrown.
我不知道人们在谈论什么,当他们说只有被抛出时才慢。
EDIT: If Exceptions aren't thrown, then that means you are doing new Exception() or something like that. Otherwise the exception is going to cause the thread to be suspended, and the stack to be walked. This may be Ok in smaller situations, but in high-traffic websites, relying on exceptions as a workflow or execution path mechanism will certainly cause you performance problems. Exceptions, per se, aren't bad, and are useful for expressing exceptional conditions
编辑:如果未抛出异常,则意味着您正在执行 new Exception() 或类似的操作。否则异常将导致线程被挂起,栈将被遍历。这在较小的情况下可能还可以,但是在高访问量的网站中,依靠异常作为工作流或执行路径机制肯定会给您带来性能问题。异常本身并不坏,可用于表达异常条件
The exception workflow in a .NET app uses first and second chance exceptions. For all exceptions, even if you are catching and handling them, the exception object is still created and the framework still has to walk the stack to look for a handler. If you catch and rethrow of course that is going to take longer - you are going to get a first-chance exception, catch it, rethrow it, causing another first-chance exception, which then doesn't find a handler, which then causes a second-chance exception.
.NET 应用程序中的异常工作流使用第一次和第二次机会异常。对于所有异常,即使您正在捕获和处理它们,仍然会创建异常对象,并且框架仍然必须遍历堆栈以查找处理程序。如果您捕获并重新抛出当然需要更长的时间 - 您将获得第一次机会异常,捕获它,重新抛出它,导致另一个第一次机会异常,然后找不到处理程序,然后导致第二次机会例外。
Exceptions are also objects on the heap - so if you are throwing tons of exceptions, then you are causing both performance and memory issues.
异常也是堆上的对象——所以如果你抛出大量异常,那么你就会导致性能和内存问题。
Furthermore, according to my copy of "Performance Testing Microsoft .NET Web Applications" written by the ACE team:
此外,根据我的 ACE 团队编写的“性能测试 Microsoft .NET Web 应用程序”副本:
"Exception handling is expensive. Execution of the involved thread is suspended while CLR recurses through the call stack in search of the right exception handler, and when it is found, the exception handler and some number of finally blocks must all have their chance to execute before regular processing can be performed."
“异常处理很昂贵。当 CLR 递归调用堆栈以寻找正确的异常处理程序时,相关线程的执行被挂起,当找到正确的异常处理程序时,异常处理程序和一定数量的 finally 块都必须有机会执行在进行常规处理之前。”
My own experience in the field showed that reducing exceptions significantly helped performance. Of course, there are other things you take into account when performance testing - for example, if your Disk I/O is shot, or your queries are in the seconds, then that should be your focus. But finding and removing exceptions should be a vital part of that strategy.
我自己在该领域的经验表明,减少异常显着提高了性能。当然,在进行性能测试时,您还需要考虑其他一些事情——例如,如果您的磁盘 I/O 被击中,或者您的查询在几秒钟内,那么这应该是您的重点。但是查找和删除异常应该是该策略的重要组成部分。
回答by Jonathan C Dickinson
My XMPP server gained a major speed boost (sorry, no actual numbers, purely observational) after I consistently tried to prevent them from happening (such as checking if a socket is connected before try to read more data) and giving myself ways to avoid them (the mentioned TryX methods). That was with only about 50 active (chatting) virtual users.
在我一直试图防止它们发生(例如在尝试读取更多数据之前检查套接字是否已连接)并给自己提供避免它们的方法之后,我的 XMPP 服务器获得了重大的速度提升(抱歉,没有实际数字,纯粹是观察性的) (提到的 TryX 方法)。那是只有大约 50 个活跃(聊天)虚拟用户。
回答by Drew Noakes
One quick note here on the performance associated with catching exceptions.
这里有一个关于与捕获异常相关的性能的快速说明。
When the execution path enters a 'try' block, nothing magical happens. There is no 'try' instruction, and no cost associated with either entering or exiting the try block. Information about the try block is stored in the method's metadata, and this metadata is used at runtime whenever an exception is raised. The execution engine walks down the stack looking for the first call that was contained in a try block. Any overhead associated with exception handling occurs only when exceptions are thrown.
当执行路径进入“try”块时,不会发生任何神奇的事情。没有“try”指令,也没有与进入或退出 try 块相关的成本。有关 try 块的信息存储在方法的元数据中,只要引发异常,就会在运行时使用此元数据。执行引擎在堆栈中查找包含在 try 块中的第一个调用。与异常处理相关的任何开销仅在抛出异常时发生。
回答by Ray Prisament
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.
只是将我自己最近的经验添加到本次讨论中:与上面所写的大部分内容一致,我发现在重复执行时抛出异常非常慢,即使没有运行调试器。我只是通过更改大约五行代码将我正在编写的大型程序的性能提高了 60%:切换到返回代码模型而不是抛出异常。诚然,有问题的代码在我更改之前已经运行了数千次,并且可能会抛出数千个异常。所以我同意上面的说法:当重要的事情实际上出错时抛出异常,而不是作为在任何“预期”情况下控制应用程序流的一种方式。