繁体   English   中英

违反一个空的lambda比检查一个潜在的null lambda更好还是更差?

[英]Is defaulting to an empty lambda better or worse than checking for a potentially null lambda?

我正在研究Java 8中的小型场景图实现。基本场景节点看起来像这样:

public class SceneNode {
  private final List<SceneNode> children = new ArrayList<>();
  protected Runnable preRender;
  protected Runnable postRender;
  protected Runnable render;

  public final void render() {
    preRender.run();
    render.run();
    for (Renderable child : children) {
      child.render();
    }
    postRender.run();
  }
}

如果Runnables默认为() -> {}这可以正常工作。 但是,或者我可以允许它们为null,但这意味着render()方法必须如下所示:

  public final void render() {
    if (null != preRender) { preRender.run(); }
    if (null != render) { render.run(); }
    for (Renderable child : children) {
      child.render();
    }
    if (null != postRender) { postRender.run(); }
  }

所以我的问题是,null检查引入的分支的隐式成本是否可能比JVM最终编译空lambda的成本更高或更低? 看起来它应该花费更多来检查null,因为潜在的分支限制了优化,而可能是Java编译器或JVM应该足够聪明以将空lambda编译成no-op。

有趣的 ,当使用-client参数运行JVM时, 检查null似乎比调用空lambda或空匿名类快一点 使用-server运行时,所有方法的性能都相同。

我用Caliper做了一个微基准测试。

这是测试类(编译时需要的最新Caliper表单git):

@VmOptions("-client")
public class EmptyLambdaTest {

    public Runnable emptyLambda = () -> {};
    public Runnable emptyAnonymousType = new Runnable() {
        @Override
        public void run() {}
    };

    public Runnable nullAbleRunnable;

    @Benchmark
    public int timeEmptyLambda(int reps){
        int dummy = 0;
        for (int i = 0; i < reps; i++) {
            emptyLambda.run();
            dummy |= i;
        }
        return dummy;
    }

    @Benchmark
    public int timeEmptyAnonymousType(int reps){
        int dummy = 0;
        for (int i = 0; i < reps; i++) {
            emptyAnonymousType.run();
            dummy |= i;
        }
        return dummy;
    }

    @Benchmark
    public int timeNullCheck(int reps){
        int dummy = 0;
        for (int i = 0; i < reps; i++) {
            if (nullAbleRunnable != null) {
                nullAbleRunnable.run();
            }
            dummy |= i;
        }
        return dummy;
    }

}

以下是基准测试结果:

违反一个空的lambda比检查一个潜在的null lambda更好还是更差?

这与询问是否更好地测试null String参数或尝试替换空String一样基本相同。

答案是,这取决于您是否要将 null视为编程错误......或者不是。

我个人认为, 意外的 null应该被视为编程错误,并且你应该允许程序与NPE崩溃。 这样,问题就会提前引起你的注意,并且更容易追踪和修复......而不是你用一些“做好”的价值来阻止NPE被抛出。

但是,当然,这不适用于预期的 null值; 即,当API javadocs说null是允许值时,并说出它意味着什么。


这也与您设计 API的方式有关。 在这种情况下,问题是你的API规范(即javadoc!)是否应该坚持程序员提供no-op lambda,或者将null视为同一个东西。 归结为以下两者之间的妥协:

  • API客户方便,
  • API实现工作,和
  • 鲁棒性; 例如,当使用错误初始化变量的值时......

我更关心使用空lambda与使用null并且必须进行空检查的运行时性能的含义。

我的直觉是测试null会更快,但性能上的任何差异都会很小,并且很可能它对应用程序的整体性能不会很重要

(UPDATE -原来,我的直觉是根据@巴尔德的微标杆“说对了一半”对于一个。 -client模式JVM,空检查是有点快,但还不足以描述有关对于一个。 -server模式JVM中, JIT编译器显然将两种情况都优化为具有相同性能的本机代码。)

我建议你认为你会(或至少应该 )处理任何潜在的优化问题:

  1. 推迟任何优化,直到您的应用程序正常运行。
  2. 对应用程序进行基准测试,看它是否已经足够快
  3. 分析应用程序以查看真正的热点所在
  4. 开发并测试推定的优化
  5. 重新运行基准测试,看看它是否有所改进
  6. 转到第2步。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM