【问题标题】:Does try catch decrease efficiency?try catch 会降低效率吗?
【发布时间】:2018-04-16 05:11:00
【问题描述】:

是否存在效率差异:-

public boolean canDivisionBeDone(int iA, int iB){

  try{
      float a = iA/iB;
    }catch(Exception e){
    return false;
 }

return true;
}

public boolean canDivisionBeDone(int iA, int iB){

 if(iB == 0){
     return false;
 }else{
         float a = iA/iB;
 }

return true;
}

如果是,为什么?

【问题讨论】:

  • try-catch 降低可读性。 return iB==0?false:true;
  • @HadiJ 我问的是性能效率。在 CPU 消耗、耗时、内存等方面。
  • 该示例不太适合比较性能。无论如何,取决于使用它的需要,你不能让它落后于它的性能。
  • @HadiJ 不必要的三元运算符也会降低可读性。 :-) return iB != 0。虽然我怀疑这只是一个 sn-p,但 OP 在计算后实际上是在用 a 做一些事情。
  • 所以我一直认为上一个链接或Is it expensive to use try-catch blocks even if an exception is never thrown? 是重复的。

标签: java performance try-catch


【解决方案1】:

using try 本身没有任何费用,但是如果 using 块产生太多异常,您应该尝试检查您的代码。

在 Java 中创建异常是一项非常缓慢的操作。预计抛出异常将花费您大约 1-5 微秒。几乎所有这些时间都花在了填充异常线程堆栈上。堆栈跟踪越深,填充它所需的时间就越长。

更多详情请阅读here

【讨论】:

    【解决方案2】:

    从编码的角度来看,我肯定更喜欢条件 (a == 0 ? 0 : (a/b)),而不是异常处理。这实际上不是异常情况,所以这里的控制流不应使用异常。

    关于效率,我写了一个微基准测试:

    @State(Scope.Thread)
    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public class MyBenchmark {
    
        private int a = 10;
        private int b = (int) Math.floor(Math.random());
    
        @Benchmark
        public float conditional() {
            if (b == 0) {
                return 0;
            } else {
                return a / b;
            }
        }
    
        @Benchmark
        public float exceptional() {
            try {
                return a / b;
            } catch (ArithmeticException aex) {
                return 0;
            }
        }
    }
    

    结果:

    Benchmark                Mode  Cnt  Score   Error  Units
    MyBenchmark.conditional  avgt  200  7.346 ± 0.326  ns/op
    MyBenchmark.exceptional  avgt  200  8.166 ± 0.448  ns/op
    

    因为我是 JMH 的新手,I am not sure my benchmark is correct。但从表面上看,“例外”方法有点慢(~10%)。老实说,我预计会有更大的不同。

    【讨论】:

    • 正如我在评论中所描述的,如果它在循环中,它确实会有所作为。
    • 您的表达式(int) Math.floor(Math.random()); 最好写成new Random().nextInt(2)。由于b 在基准测试期间是固定的,因此不会出现分支错误预测,这不太现实。使用 int[] b = new Random().ints(1000, 0, 100).mapToInt(n -> n<percentage ? 0 : 1).toArray() 将允许您使用零百分比对基准进行参数化。
    • @SreekanthKarumanaghat 如果这些数字是正确的(我实际上认为分支错误预测点很重要,所以实际成本会更高),那 10% 的差异仍然只有 0.8 左右 nano i> 秒。因此,在 10,000 次迭代的循环中,总共会增加大约 0.0082 毫秒。它有所作为,但非常微小。这就是人们警告不要过早优化的原因:很有可能你会在一些真正无关紧要的事情上节省 10% 甚至 90%。
    • (另外,基准测试:Math.floor(Math.random()) 不只是 0 吗?你从数字 0 ≤ r
    • @maaartinus 不,Math.floor(Math.random()) 不等于 new Random().nextInt(2)Math.floor(Math.random()) 总是给出 0。
    【解决方案3】:

    异常适用于您想要传播回调用者的异常情况。在正常操作期间不要依赖异常。

    我只想把你的代码写成:

    public boolean canDivisionBeDone(int iA, int iB) {
        return iB != 0;
    }
    

    但要回答您的问题:try-catch-finally 是在 Java 中使用离线异常表实现的,因此,当 没有抛出异常时,@987654321 @。

    这两个函数的字节码如下所示:

      public boolean canDivisionBeDoneTryCatch(int, int);
        Code:
           0: iload_1
           1: iload_2
           2: idiv
           3: i2f
           4: fstore_3
           5: goto          11
           8: astore_3
           9: iconst_0
          10: ireturn
          11: iconst_1
          12: ireturn
        Exception table:
           from    to  target type
               0     5     8   Class java/lang/Exception
    
      public boolean canDivisionBeDoneIf(int, int);
        Code:
           0: iload_2
           1: ifne          6
           4: iconst_0
           5: ireturn
           6: iload_1
           7: iload_2
           8: idiv
           9: i2f
          10: fstore_3
          11: iconst_1
          12: ireturn
    

    如您所见,幸福的道路几乎相同。

    但是,抛出异常是昂贵的。

    所以是的,我预计异常版本会稍微慢一些,具体取决于iB == 0 情况的比率。

    如有疑问,请对其进行基准测试。

    【讨论】:

      猜你喜欢
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      • 2017-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-16
      相关资源
      最近更新 更多