C# 试抓表演
MSDN上的一篇文章指出,只要不引发实际异常,您可以使用任意数量的try-catch块,并且不会产生任何性能成本。C# 试抓表演,c#,performance,C#,Performance,MSDN上的一篇文章指出,只要不引发实际异常,您可以使用任意数量的try-catch块,并且不会产生任何性能成本。 因为我一直认为,即使没有抛出异常,try-catch也会对性能造成很小的影响,所以我做了一个小测试 private void TryCatchPerformance() { int iterations = 100000000; Stopwatch stopwatch = Stopwatch.StartNew();
因为我一直认为,即使没有抛出异常,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));
}
因此,使用无尝试捕获块似乎毕竟更快?我发现更奇怪的是,当我用更复杂的东西替换for循环体中的计算时,比如:
c+=I*(2*(int)Math.Floor((double)I))代码>
差别远没有那么显著。
With try catch: 640
Without try catch: 655
我是做错了什么,还是有一个合乎逻辑的解释?实际的计算是如此之小,以至于精确的测量非常棘手。在我看来,try-catch可能会在常规训练中增加一点固定的额外时间。我不知道异常是如何在C#中实现的,我冒昧地猜测,这主要是异常路径的初始化,可能只是JIT上的一个轻微负载
对于任何实际使用,花在计算上的时间都会超过摆弄try-catch所花的时间,因此try-catch的成本几乎为零。JIT不会对“受保护的”/“try”块执行优化,我猜这取决于您在try/catch块中编写的代码,这将影响您的性能。请注意,我只有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 ;
}
}
分解这些:
// 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
及
我看到的主要区别是,a.cs在IL_0006时直接进入ret
,而b.cs必须在IL_0013时离开。对于我的示例,我的最佳猜测是,leave
在编译为机器代码时是一个(相对)昂贵的跳转——可能是这样,也可能不是这样,尤其是在for循环中。也就是说,try-catch没有固有的开销,但是跳过catch会有成本,就像任何条件分支一样。有关try/catch块如何工作的讨论,请参阅,
有些实现的开销很高,有些实现的开销为零,
当没有异常发生时。在优化的发布程序集中,try/catch/finally/fault块本身基本上没有开销。虽然通常会为catch和finally块添加额外的IL,但当没有抛出异常时,行为上几乎没有差异。与简单的ret不同,通常会有一个假期留给以后的ret
try/catch/finally块的真正成本发生在处理异常时。在这种情况下,必须创建异常,必须放置堆栈爬网标记,并且,如果处理异常并访问其StackTrace属性,则会发生堆栈遍历。最重要的操作是堆栈跟踪,它遵循先前设置的堆栈爬网标记来建立一个StackTrace对象,该对象可用于显示错误发生的位置及其通过的调用
如果在尝试/捕获块中没有任何行为,那么“留待重试”与仅仅“重试”的额外成本将占主导地位,并且明显存在可测量的差异。但是,在try子句中存在某种行为的任何其他情况下,块本身的成本将完全被抵消 仅34毫秒的差异小于此类测试的误差幅度
正如您所注意到的,当您增加测试的持续时间时,差异就消失了,两组代码的性能实际上是相同的
在进行这种基准测试时,我尝试在代码的每一部分上循环至少20秒,最好更长,最好是几个小时。问题首先在于测试代码。
您使用了stopwatch.appeased.millizes,它只显示所用时间的毫秒部分,
使用Total毫秒获取整个零件
如果没有抛出异常,则差异最小
但真正的问题是“我需要检查异常还是让C#处理异常抛出?”
显然。。。独自处理。。。
尝试运行以下命令:
private void TryCatchPerformance()
{
int iterations = 10000;
textBox1.Text = "";
Stopwatch stopwatch = Stopwatch.StartNew();
int c = 0;
for (int i = 0; i < iterations; i++)
{
try
{
c += i / (i % 50);
}
catch (Exception)
{
}
}
stopwatch.Stop();
Debug.WriteLine(String.Format("With try catch: {0}", stopwatch.Elapsed.TotalSeconds));
Stopwatch stopwatch2 = Stopwatch.StartNew();
int c2 = 0;
for (int i = 0; i < iterations; i++)
{
int iMod50 = (i%50);
if(iMod50 > 0)
c2 += i / iMod50;
}
stopwatch2.Stop();
Debug.WriteLine( String.Format("Without try catch: {0}", stopwatch2.Elapsed.TotalSeconds));
}
private void TryCatchPerformance()
{
int迭代次数=10000次;
textBox1.Text=“”;
秒表秒表=Stopwatch.StartNew();
int c=0;
对于(int i=0;i0)
c2+=i/iMod50;
}
秒表2.Stop();
WriteLine(String.Format(“不带try-catch:{0}”,stopwatch2.appeased.TotalSeconds));
}
输出:
过时:看下面强>
带try catch:1.9938401
不带试接:8.92E-05
令人惊讶的是,只有10000个对象,还有200个例外
更正:
我在“调试”和“VS写入异常到输出”窗口上运行代码。。
这些是发布的结果
开销大大减少,但仍有7500%的改进
使用try catch:0.0546915
单独检查:0.0007294
通过try-catch抛出我自己的异常对象:0.0265229,您只不过证明了基准测试比看起来要困难得多。对于初学者,您应该在两个模块反转的情况下重复测量。反转两个模块不会在输出中产生任何显著差异。你能详细解释一下为什么这很重要吗?晚会有点晚了。但这是因为JIT可能已经做出了影响第二次运行的优化决策。+1的努力,但for循环实际上是domi
// 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
private void TryCatchPerformance()
{
int iterations = 10000;
textBox1.Text = "";
Stopwatch stopwatch = Stopwatch.StartNew();
int c = 0;
for (int i = 0; i < iterations; i++)
{
try
{
c += i / (i % 50);
}
catch (Exception)
{
}
}
stopwatch.Stop();
Debug.WriteLine(String.Format("With try catch: {0}", stopwatch.Elapsed.TotalSeconds));
Stopwatch stopwatch2 = Stopwatch.StartNew();
int c2 = 0;
for (int i = 0; i < iterations; i++)
{
int iMod50 = (i%50);
if(iMod50 > 0)
c2 += i / iMod50;
}
stopwatch2.Stop();
Debug.WriteLine( String.Format("Without try catch: {0}", stopwatch2.Elapsed.TotalSeconds));
}