MSDN上的这篇文章指出,您可以根据需要使用尽可能多的try catch块,并且不会产生任何性能成本,因为不会引发任何实际异常.
因为我总是认为即使没有抛出异常,try-catch总是会受到很小的性能影响,所以我做了一点测试.
private void TryCatchPerformance()
{
int iterations = 100000000;
Stopwatch stopwatch = Stopwatch.StartNew();
int c = 0;
for (int i = 0; i < iterations; i++)
{
try
{
// c += i * (2 * (int)Math.Floor((double)i));
c += i * 2;
}
catch (Exception ex)
{
throw;
}
}
stopwatch.Stop();
WriteLog(String.Format("With try catch: {0}", stopwatch.ElapsedMilliseconds));
Stopwatch stopwatch2 = Stopwatch.StartNew();
int c2 = 0;
for (int i = 0; i < iterations; i++)
{
// c2 += i * (2 * (int)Math.Floor((double)i));
c2 += i * 2;
}
stopwatch2.Stop();
WriteLog(String.Format("Without try catch: {0}", stopwatch2.ElapsedMilliseconds));
}
Run Code Online (Sandbox Code Playgroud)
我得到的输出:
With try catch: 68
Without try catch: 34
Run Code Online (Sandbox Code Playgroud)
所以似乎没有使用try-catch块似乎更快?
我发现更奇怪的是,当我用更复杂的东西替换for循环体中的计算时:c += i * (2 * (int)Math.Floor((double)i));
差异远远不那么显着.
With try catch: 640
Without try catch: 655
Run Code Online (Sandbox Code Playgroud)
我在这里做错了还是有合理的解释?
P.K*_*P.K 15
JIT不对'protected'/'try'块执行优化,我想根据你在try/catch块中编写的代码,这会影响你的性能.
jri*_*sta 14
try/catch/finally/fault块本身在优化的发布程序集中基本上没有开销.虽然通常会为catch和finally块添加额外的IL,但是当没有抛出异常时,行为几乎没有差异.而不是一个简单的ret,通常有一个后来退休的假.
处理异常时会发生try/catch/finally块的真实成本.在这种情况下,必须创建异常,必须放置堆栈爬网标记,并且,如果处理异常并访问其StackTrace属性,则会引发堆栈遍历.最重的操作是堆栈跟踪,它跟随先前设置的堆栈爬网标记以构建StackTrace对象,该对象可用于显示错误发生的位置以及它通过的调用.
如果在try/catch块中没有任何行为,那么'leave to ret'与'ret'的额外成本将占主导地位,并且显然会有一个可衡量的差异.但是,在try子句中存在某种行为的任何其他情况下,块本身的成本将完全被否定.
请注意,我只提供Mono:
// a.cs
public class x {
static void Main() {
int x = 0;
x += 5;
return ;
}
}
// b.cs
public class x {
static void Main() {
int x = 0;
try {
x += 5;
} catch (System.Exception) {
throw;
}
return ;
}
}
Run Code Online (Sandbox Code Playgroud)
拆卸这些:
// a.cs
default void Main () cil managed
{
// Method begins at RVA 0x20f4
.entrypoint
// Code size 7 (0x7)
.maxstack 3
.locals init (
int32 V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: ldc.i4.5
IL_0004: add
IL_0005: stloc.0
IL_0006: ret
} // end of method x::Main
Run Code Online (Sandbox Code Playgroud)
和
// b.cs
default void Main () cil managed
{
// Method begins at RVA 0x20f4
.entrypoint
// Code size 20 (0x14)
.maxstack 3
.locals init (
int32 V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
.try { // 0
IL_0002: ldloc.0
IL_0003: ldc.i4.5
IL_0004: add
IL_0005: stloc.0
IL_0006: leave IL_0013
} // end .try 0
catch class [mscorlib]System.Exception { // 0
IL_000b: pop
IL_000c: rethrow
IL_000e: leave IL_0013
} // end handler 0
IL_0013: ret
} // end of method x::Main
Run Code Online (Sandbox Code Playgroud)
我看到的主要区别是a.cs去直接ret在IL_0006,而b.cs必须leave IL_0013在IL_006.我与最好的猜测我的例子,就是leave当编译成机器代码(相对)昂贵的跳跃-这可能会或可能不会是这样,尤其是在你的循环.也就是说,try-catch没有固有的开销,但跳过 catch会有成本,就像任何条件分支一样.
实际计算量非常小,以至于精确测量非常棘手。在我看来,try catch 可能会为例程增加很少的固定额外时间。我会冒险猜测,不知道如何在 C# 中实现异常,这主要是异常路径的初始化,也许只是 JIT 上的轻微负载。
对于任何实际使用,花费在计算上的时间将压倒花在摆弄 try-catch 上的时间,以至于 try-catch 的成本可以接近于零。
| 归档时间: |
|
| 查看次数: |
17961 次 |
| 最近记录: |