JC *_*bbs 92 .net c# optimization performance try-catch
所以,我知道try/catch会增加一些开销,因此不是控制流程流的好方法,但是这种开销来自何处以及它的实际影响是什么?
Sha*_*tin 96
要点三点:
首先,在代码中实际使用try-catch块几乎没有或没有性能损失.在尝试避免将它们放入您的应用程序时,这不应该是一个考虑因素.只有在抛出异常时,性能才会发挥作用.
当异常,除了栈展开该采取哪些其他人所说的地点操作等被抛出,你应该知道的运行时间/反射相关的东西一大堆发生在以填充异常类的成员,如堆栈跟踪对象和各种类型的成员等
我相信这是为什么一般建议,如果你要重新抛出异常的原因之一是,throw;而不是再次抛出异常或构造一个新异常,因为在这些情况下所有的堆栈信息都被重新调整而在简单中把它全部保留下来.
Mik*_*one 50
我不是语言实现方面的专家(所以请稍等一下),但我认为最大的成本之一是展开堆栈并将其存储为堆栈跟踪.我怀疑只有当抛出异常时才会发生这种情况(但我不知道),如果是这样的话,每次抛出异常时这都会有相当大的隐藏成本......所以它不像你只是从一个地方跳过在另一个代码中,有很多事情要发生.
只要您使用EXCEPTIONAL行为的异常(因此不是典型的,预期的程序路径),我认为这不是问题.
Roa*_*ior 19
您是否询问在未抛出异常时使用try/catch/finally的开销,或者使用异常来控制流程的开销?后者有点类似于使用一根炸药点燃幼儿的生日蜡烛,相关的开销分为以下几个方面:
由于抛出的异常访问非常驻代码和数据通常不在应用程序的工作集中,因此可能会出现其他页面错误.
上述两个项目通常都会访问"冷"代码和数据,因此如果您有内存压力,则很可能出现硬页面错误:
至于成本的实际影响,这可能会有很大差异,具体取决于当时代码中的其他内容.Jon Skeet 在这里有一个很好的总结,有一些有用的链接.我倾向于同意他的观点,即如果你达到了异常会严重影响你的表现的程度,那么你在使用异常方面就会遇到问题而不仅仅是表现.
根据我的经验,最大的开销是实际抛出异常并处理它.我曾经在一个项目中工作,其中使用类似于以下的代码来检查某人是否有权编辑某个对象.这个HasRight()方法在表示层中的任何地方都使用,并且通常被称为100个对象.
bool HasRight(string rightName, DomainObject obj) {
try {
CheckRight(rightName, obj);
return true;
}
catch (Exception ex) {
return false;
}
}
void CheckRight(string rightName, DomainObject obj) {
if (!_user.Rights.Contains(rightName))
throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)
当测试数据库充分利用测试数据时,这会在打开新表格等时导致非常明显的减速.
因此,我将其重构为以下内容,根据后来的快速测量结果,它大约快2个数量级:
bool HasRight(string rightName, DomainObject obj) {
return _user.Rights.Contains(rightName);
}
void CheckRight(string rightName, DomainObject obj) {
if (!HasRight(rightName, obj))
throw new Exception();
}
Run Code Online (Sandbox Code Playgroud)
因此,简而言之,在正常流程中使用异常比使用类似的流程流程慢两个数量级,没有例外.
与普遍接受的理论相反,try/catch可能会对性能产生重大影响,那就是是否抛出异常!
前者已被覆盖由微软MVP的一对夫妇的博客文章多年来,我相信你可以很容易地找到他们又StackOverflow的关心这么多有关内容,所以我会提供链接到一些他们作为填充物的证据:
try/ catch/finally(和第二部分)的性能影响,作者 Peter Ritchie 探讨了try/catch/finally禁用的优化(我将用标准中的引号进一步探讨这一点)ParseTryParseConvertToIan Huff 的Performance Profiling vs. vs.公然指出“异常处理非常慢”,并通过相互竞争Int.Parse和Int.TryParse对抗来证明这一点......对于坚持在幕后TryParse使用try/ 的人来说catch,这应该说明一些问题!还有这个答案显示了使用和不使用try/ 的反汇编代码之间的区别catch。
它似乎很明显,有是一个开销,这是在代码生成公然观察到的,那开销甚至似乎是承认人谁微软的价值!然而我是,重复互联网......
是的,对于一行微不足道的代码,有许多额外的 MSIL 指令,而且这甚至没有涵盖禁用的优化,因此从技术上讲,它是一种微优化。
几年前我发布了一个答案,因为它专注于程序员的生产力(宏观优化)而被删除。
这是不幸的,因为在这里和那里没有节省几纳秒的 CPU 时间很可能弥补人类手动优化的许多累积小时。你的老板为哪一个付出更多:你的一小时时间,还是电脑运行一小时?我们什么时候拔掉插头并承认是时候购买一台速度更快的电脑了?
显然,我们应该优化我们的优先级,而不仅仅是我们的代码!在我的最后一个答案中,我利用了两个代码片段之间的差异。
使用try/ catch:
int x;
try {
x = int.Parse("1234");
}
catch {
return;
}
// some more code here...
Run Code Online (Sandbox Code Playgroud)
不使用try/ catch:
int x;
if (int.TryParse("1234", out x) == false) {
return;
}
// some more code here
Run Code Online (Sandbox Code Playgroud)
从维护开发人员的角度考虑,如果不是在分析/优化(上面涵盖)中,如果不是try/catch问题,甚至可能不需要,那么这更有可能浪费您的时间,然后滚动浏览源代码......其中之一有四行额外的样板垃圾!
随着越来越多的字段被引入到一个类中,所有这些样板垃圾的积累(在源代码和反汇编代码中)远远超出了合理的水平。每个字段多出四行,而且它们总是相同的行......我们没有教过避免重复自己吗?我想我们可以将try/catch隐藏在一些自制的抽象之后,但是......那么我们不妨避免异常(即 use Int.TryParse)。
这甚至不是一个复杂的例子;我见过在try/中实例化新类的尝试catch。考虑到构造函数内的所有代码可能会被取消某些优化的资格,否则编译器会自动应用这些优化。有什么更好的方法可以产生编译器很慢的理论,而不是编译器完全按照它的要求去做?
假设所述构造函数抛出异常,并因此触发了一些错误,那么糟糕的维护开发人员必须对其进行跟踪。这可能不是一件容易的事,因为与goto噩梦的意大利面条式代码不同,try/catch会在三个维度上造成混乱,因为它不仅可以将堆栈向上移动到同一方法的其他部分,还可以移动到其他类和方法,所有这些都将由维护开发人员观察到,很难!然而我们被告知“goto 是危险的”,呵呵!
最后我提到,try/catch有它的好处,即它旨在禁用优化!如果您愿意,它是调试辅助工具!这就是它的设计目的,也是它应该用作...
我想这也是一个积极的观点。它可用于禁用优化,否则可能会削弱多线程应用程序的安全、健全的消息传递算法,并捕获可能的竞争条件 ;) 这是我能想到的唯一使用 try/catch 的场景。甚至还有其他选择。
什么做的优化try,catch并finally禁用?
又名
怎么样try,catch和finally作为调试工具有用吗?
它们是写屏障。这来自标准:
12.3.3.13 Try-catch 语句
对于以下形式的语句stmt:
Run Code Online (Sandbox Code Playgroud)try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n
- try-block开始时v的明确赋值状态与stmt开始时v的明确赋值状态相同。
- v在catch-block-i开头的明确赋值状态(对于任何i)与v在stmt开头的明确赋值状态相同。
- 的明确赋值状态v在终点语句是明确赋值当(且仅当)v是在结束点明确分配的try块和每一个捕获块-I (对于每我从1到Ñ)。
换句话说,在每个try语句的开头:
try语句之前对可见对象所做的所有分配都必须完成,这需要线程锁定才能启动,这对于调试竞争条件非常有用!try语句之前明确分配的未使用的变量分配每个catch陈述都有一个类似的故事;假设在您的try语句(或它调用的构造函数或函数等)中,您分配给该无意义的变量(比方说,garbage=42;),编译器无法消除该语句,无论它与程序的可观察行为多么无关. 分配需要在进入块之前完成catch。
就其价值而言,finally讲述了一个同样有辱人格的故事:
12.3.3.14 Try-finally 语句
对于以下形式的try语句stmt:
Run Code Online (Sandbox Code Playgroud)try try-block finally finally-block• try-block 开始时v的明确赋值状态与stmt开始时v的明确赋值状态相同。 • finally-block开始时v的明确赋值状态与stmt开始时v的明确赋值状态相同。 •当(且仅当)以下任一条件时,v在stmt端点的明确赋值状态是明确赋值的: o v在try-block o v的端点被明确赋值
在终点是明确赋值最后块 如果控制流转移(如转到语句)由内开始试块,并且外端部的try块,然后v也视为已明确对分配如果v在finally-block的端点明确分配,则控制流转移。(这不是唯一的 if——如果v在此控制流传输中由于其他原因被明确分配,那么它仍然被认为是明确分配的。)
12.3.3.15 Try-catch-finally 语句
形式的try - catch - finally语句的明确赋值分析:
Run Code Online (Sandbox Code Playgroud)try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n finally finally-block就像语句是包含try - catch语句的try - finally语句一样完成:
Run Code Online (Sandbox Code Playgroud)try { try try-block catch ( ... ) catch-block-1 ... catch ( ... ) catch-block-n } finally finally-block