什么会空渔获物和抛块呢?会空渔获物、抛块呢

2023-09-07 08:50:54 作者:李之家庭

我知道下面渔获物和抛块是多余的,我很好奇什么样的伤害可以做?

可以编译器优化也走在释放模式?或者,它只是反正捕获异常并重新抛出呢?如果是后一种情况,什么样的性能损失的会引起?

 尝试
{
  // ...
}
抓住 {
  扔;
}
 

解决方案

优化

编译器不会优化了这一点,即使是在一个发布版本。

看看下面的测试应用程序:

 公共类节目{
    公共静态无效的主要(字串[] args){
        尝试 {
            CallsThrow();
        }
        赶上(例外前){
            Console.WriteLine(主()捕获的异常:+ ex.Message);
        }
        Console.Read();
    }

    私有静态无效CallsThrow(){
        尝试 {
            扔();
        }
        抓住 {
            扔;
        }
    }

    私有静态无效掷(){
        抛出新的异常(这是我的例外。);
    }
}
 

使用 ILSpy 我们可以看看在输出二进制的的 IL 水平。我们看到,尝试 / CallsThrow 仍是在我们发布的二进制有:

 。方法私人hidebysig静
    无效CallsThrow()CIL管理
{
    //方法开始于RVA 0x2094
    // code尺寸11(0XB)
    .maxstack 1

    。尝试
    {
        IL_0000:调用无效JunkCSharpConsole.Program ::掷()
        IL_0005:leave.s IL_000a
    } //结束。尝试
    赶上[mscorlib程序] System.Object的
    {
        IL_0007:流行
        IL_0008:重新抛出
    } //结束处理

    IL_000a:RET
法计划} //结束:: CallsThrow
 
冬季低温钓鱼渔获不会太差,牢记这三点,很简单,但是你忽略了

基准

code:

 公共类节目
{
    公共静态无效的主要(字串[] args){
        const int的N =亿;
#如果调试
        常量字符串模式=调试;
#其他
        常量字符串模式=释放;
#ENDIF

        Console.WriteLine(测试{0}在迭代{1}模式:,N模式);

        //尝试JIT /缓存
        CallsThrowWithTryCatch(假);
        CallsThrowWithoutTryCatch(假);

        //用try / catch语句+掷测试
        变种S1 = Stopwatch.StartNew();
        的for(int i = 0; I&n种;我++)
            CallsThrowWithTryCatch(假);
        s1.Stop();
        Console.WriteLine(随着try / catch语句:{0}毫秒,s1.ElapsedMilliseconds);

        //测试没有的try / catch +掷
        变种S2 = Stopwatch.StartNew();
        的for(int i = 0; I&n种;我++)
            CallsThrowWithoutTryCatch(假);
        s2.Stop();
        Console.WriteLine(没有的try / catch {0}毫秒,s2.ElapsedMilliseconds);

        VAR PCT =(s1.ElapsedMilliseconds  -  s2.ElapsedMilliseconds)/(双)s1.ElapsedMilliseconds * 100.0;
        Console.WriteLine(没有的try / catch快了{0:0.02}%,PCT);

        //只是表明它的工作原理
        尝试 {
            CallsThrowWithTryCatch(真正的);
        }
        赶上(例外前){
            Console.WriteLine(主()捕获的异常:+ ex.Message);
        }

        //等待退出
        Console.WriteLine(preSS ENTER退出。);
        Console.Read();
    }

    私有静态无效CallsThrowWithTryCatch(布尔doThrow){
        尝试 {
            投掷(doThrow);
        }
        抓住 {
            扔;
        }
    }

    私有静态无效CallsThrowWithoutTryCatch(布尔doThrow){
        投掷(doThrow);
    }

    私有静态无效掷(布尔doThrow){
        如果(doThrow)
            抛出新的异常(这是我的例外。);
    }
}
 

结果:

测试亿反复调试模式:   随着try / catch语句:1492毫秒   如果没有的try / catch:1474毫秒 没有的try / catch快了1.22% 主()捕获的异常:这是我的异常。 preSS ENTER退出。 测试亿反复在发行方式:   随着try / catch语句:598毫秒   如果没有的try / catch:458毫秒 没有的try / catch快了23.42% 主()捕获的异常:这是我的异常。 preSS ENTER退出。

我们可以看到,对了,还有的是的关联的性能损失,甚至空尝试 / 。在调试版本也不是那么显著,但发布版本呈大幅23.42%的改善通过删除尝试 /

I know the following catch and throw block is redundant, I am curious what kind of damage it can do ?

Can compiler optimizes it away in release mode? Or it will just catch the exception anyway and rethrow it ? If it is the latter case, what kind of performance penalty it will cause ?

try
{
  //...
}
catch {
  throw;
}

解决方案

Optimization

The compiler will not optimize this out, even in a Release build.

Take the following test application:

public class Program {
    public static void Main(string[] args) {
        try {
            CallsThrow();
        }
        catch(Exception ex) {
            Console.WriteLine("Main() caught exception: " + ex.Message);
        }
        Console.Read();
    }

    private static void CallsThrow() {
        try {
            Throw();
        }
        catch {
            throw;
        }
    }

    private static void Throw() {
        throw new Exception("Here's my exception.");
    }
}

Using ILSpy we can look at the output binary at the IL level. We see that the try/catch in CallsThrow is still there in our Release binary:

.method private hidebysig static 
    void CallsThrow () cil managed 
{
    // Method begins at RVA 0x2094
    // Code size 11 (0xb)
    .maxstack 1

    .try
    {
        IL_0000: call void JunkCSharpConsole.Program::Throw()
        IL_0005: leave.s IL_000a
    } // end .try
    catch [mscorlib]System.Object
    {
        IL_0007: pop
        IL_0008: rethrow
    } // end handler

    IL_000a: ret
} // end of method Program::CallsThrow

Benchmark

Code:

public class Program
{
    public static void Main(string[] args) {
        const int N = 100000000;
#if DEBUG
        const string mode = "Debug";
#else
        const string mode = "Release";
#endif

        Console.WriteLine("Testing {0} iterations in {1} mode:", N, mode);

        // Attempt to JIT / cache
        CallsThrowWithTryCatch(false);
        CallsThrowWithoutTryCatch(false);

        // Test with try/catch+throw
        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < N; i++ )
            CallsThrowWithTryCatch(false);
        s1.Stop();
        Console.WriteLine("  With try/catch: {0} ms", s1.ElapsedMilliseconds);

        // Test without try/catch+throw
        var s2 = Stopwatch.StartNew();
        for (int i = 0; i < N; i++)
            CallsThrowWithoutTryCatch(false);
        s2.Stop();
        Console.WriteLine("  Without try/catch: {0} ms", s2.ElapsedMilliseconds);

        var pct = (s1.ElapsedMilliseconds - s2.ElapsedMilliseconds) / (double)s1.ElapsedMilliseconds * 100.0;
        Console.WriteLine("No try/catch faster by {0:.02}%", pct);

        // Just show that it works
        try {
            CallsThrowWithTryCatch(true);
        }
        catch (Exception ex) {
            Console.WriteLine("Main() caught exception: " + ex.Message);
        }

        // Wait to exit
        Console.WriteLine("Press ENTER to exit.");
        Console.Read();
    }

    private static void CallsThrowWithTryCatch(bool doThrow) {
        try {
            Throw(doThrow);
        }
        catch {
            throw;
        }
    }

    private static void CallsThrowWithoutTryCatch(bool doThrow) {
        Throw(doThrow);
    }

    private static void Throw(bool doThrow) {
        if (doThrow)
            throw new Exception("Here's my exception.");
    }
}

Results:

Testing 100000000 iterations in Debug mode:
  With try/catch: 1492 ms
  Without try/catch: 1474 ms
No try/catch faster by 1.22%
Main() caught exception: Here's my exception.
Press ENTER to exit.

Testing 100000000 iterations in Release mode:
  With try/catch: 598 ms
  Without try/catch: 458 ms
No try/catch faster by 23.42%
Main() caught exception: Here's my exception.
Press ENTER to exit.

We can see that yes, there is a performance penalty associated with even the empty try/catch. In Debug builds it is not so significant, but a Release build showed a substantial 23.42% improvement by removing the try/catch.

相关推荐