为什么用功能包装器捕获方法调用链的异常似乎比手动检查要昂贵得多?

问题描述

我正在考虑对所有可能在某个时候返回空值的方法调用链使用如下函数包装器的想法:

public static <T,R extends Class> R getorPassNullable(Function<T,R> call,T p@R_404_6460@m) {
try {
  return call.apply(p@R_404_6460@m);
}
catch (NullPointerException e) {
  return null;
}
}

我想出了一个简单的基准来测试对性能的影响:创建一个虚拟变量,其中一些字段嵌套2-3个调用,然后使用函数包装器和普通的手动检查来测量运行时间。

对于基准测试本身,我稍微修改了一些其他帖子的答案,以简单地多次执行给定方法

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",10));
  }
 

我原本希望对性能有一些影响,但结果却是巨大的:手动检查需要9毫秒,而函数包装器需要约7000毫秒。

snapshot from user console

我不确定为什么这里的差异有多个数量级。我在设置方法或实现包装程序方面缺少什么,还是在功能接口方面使它们使用起来如此缓慢?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)