为什么用函数 Package 器捕捉方法调用链的异常要比手动检查昂贵得多?

sqougxex  于 2021-07-12  发布在  Java
关注(0)|答案(0)|浏览(205)

我当时的想法是,对任何可能在某个时候返回空值的方法调用链使用如下函数 Package 器:

public static <T, R extends Class> R getOrPassNullable(Function<T, R> call, T param) {
try {
  return call.apply(param);
}
catch (NullPointerException e) {
  return null;
}
}

我提出了一个简单的基准测试来测试性能影响:创建一个虚拟变量,其中一些字段嵌套在2-3个深度调用中,并使用我的函数 Package 器和普通的手动检查来度量运行时。
对于基准测试本身,我稍微修改了其他帖子中的一个答案,只需按照指定次数执行给定的方法:

public class TimeTracedExecutor<T, R> {
    Function<T, R> methodToExecute;
    public TimeTracedExecutor(Function<T, R> methodToExecute) {
      this.methodToExecute = methodToExecute;
    }
    public void executeWithInput(String taskDescription, T t, int times) {
      Instant start = Instant.now();
      for (int i = 0; i < times; i++) {
        methodToExecute.apply(t);
      }
      Instant finish = Instant.now();
      String format = "Task took %s milliseconds: " + taskDescription;
      String elapsedTime = NumberFormat.getNumberInstance()
          .format(Duration.between(start, finish).toMillis());
      System.out.println(String.format(format, elapsedTime));
    }
}

然后在测试用例中,我使用了一个简单的类定义和嵌套字段,如下所示:

public class NestingDoll {
  private NestingDoll nested;

  public NestingDoll getNested(){
    return this.nested;   
  }

  public void setNested(NestingDoll val){
    this.nested = val;
  }
}

对于要测试的实现,我使用了以下两种方法:

private NestingDoll getNestedFunctionalBypass(NestingDoll root) {
    return getOrPassNullable(() -> root.getNested().getNested().getNested().getNested());
  }

  private NestingDoll getNestedManualChecks(NestingDoll root) {
    if (root != null
        && root.getNested() != null
        && root.getNested().getNested() != null
        && root.getNested().getNested().getNested() != null)
      ) {
      return root.getNested().getNested().getNested().getNested();
    }
    return null;
  }

最后,我建立并运行了如下基准:

public void profileNullChecks() {
    NestingDoll root = new NestingDoll();
    NestingDoll depth1 = new NestingDoll();
    NestingDoll depth2 = new NestingDoll();
    NestingDoll depth3 = new NestingDoll();
    depth2.setNested(depth3);
    depth1.setNested(depth2);
    root.setNested(depth1);

    TimeTracedExecutor<NestingDoll, NestingDoll> nullChecks =
        new TimeTracedExecutor<NestingDoll,NestingDoll>(
            this::getNestedManualChecks);
    TimeTracedExecutor<NestingDoll, NestingDoll> functionalBypass =
        new TimeTracedExecutor<NestingDoll,NestingDoll>(
            this::getNestedFunctionalBypass);
    nullChecks.executeWithInput("Manual null checks", root, (int) Math.pow(10, 10));
    functionalBypass.executeWithInput("Functional bypass", root, (int) Math.pow(10, 10));
  }

我本以为可能会对性能产生一些影响,但结果却是巨大的:手动检查的时间是9毫秒,而我的函数 Package 器的时间是7000毫秒。

我不知道为什么这里的差异有多个数量级。我的设置方式或实现 Package 器的方式是否有什么遗漏,或者功能接口是否有什么让它们使用起来如此缓慢?

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题