如何解决为什么用功能包装器捕获方法调用链的异常似乎比手动检查要昂贵得多?
我正在考虑对所有可能在某个时候返回空值的方法调用链使用如下函数包装器的想法:
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个调用,然后使用函数包装器和普通的手动检查来测量运行时间。
对于基准测试本身,我稍微修改了一些其他帖子的答案,以简单地多次执行给定方法:
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毫秒。
我不确定为什么这里的差异有多个数量级。我在设置方法或实现包装程序方面缺少什么,还是在功能接口方面使它们使用起来如此缓慢?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。