java - java Lambda 与匿名类之间的执行时间差异很大

标签 java performance lambda closures java-8

我很好奇针对同一个匿名类创建 java8 lambda 实例的性能。 (在 win32 java build 1.8.0-ea-b106 上进行的测量)。我创建了一个非常简单的示例,并测量了 java 在创建 lambda 表达式时是否建议对 new 运算符进行一些优化:

static final int MEASURES = 1000000;
static interface ICallback{
    void payload(int[] a);
}
/**
* force creation of anonymous class many times
*/
static void measureAnonymousClass(){
    final int arr[] = {0};
    for(int i = 0; i < MEASURES; ++i){
        ICallback clb = new ICallback() {
            @Override
            public void payload(int[] a) {
                a[0]++;
            }
        };
        clb.payload(arr);
    }
}
/**
* force creation of lambda many times 
*/
static void measureLambda(){ 
    final int arr[] = {0};
    for(int i = 0; i < MEASURES; ++i){
        ICallback clb = (a2) -> {
            a2[0]++;
        };
        clb.payload(arr);
    }
}

(完整代码可在此处获取:http://codepad.org/Iw0mkXhD)结果相当可预测 - lambda 获胜 2 次。

但真正做出的转变很少closure显示 lambda 的时间非常糟糕。匿名类赢了10次! 所以现在匿名类看起来像:

ICallback clb = new ICallback() {
        @Override
        public void payload() {
            arr[0]++;
        }
    };

lambda 的作用如下:

ICallback clb = () -> {
            arr[0]++;
        };

(完整代码可在此处获取:http://codepad.org/XYd9Umty) 谁能解释一下为什么在处理关闭方面存在如此大(坏)的差异?

最佳答案

更新

一些评论怀疑我在底部的基准测试是否有缺陷 - 在引入大量随机性(以防止 JIT 优化太多东西)之后,我仍然得到类似的结果,所以我倾向于认为它是好的。

与此同时,我遇到了this presentation由 lambda 实现团队。第 16 页显示了一些性能数据:内部类和闭包具有相似的性能/非捕获 lambda 最多快 5 倍。

@StuartMarks 发布了这个 JVMLS 2013 talk from Sergey Kuksenko on lambda performance .最重要的是,后 JIT 编译、lambda 和匿名类在当前 Hostpot JVM 实现上的表现相似。


您的基准

正如您发布的那样,我也运行了您的测试。问题是第一种方法只运行 20 毫秒,第二种方法只运行 2 毫秒。虽然这是 10:1 的比例,但它不具有代表性,因为测量时间太短了。

然后我修改了你的测试以允许更多的 JIT 预热,我得到了与 jmh 相似的结果(即匿名类和 lambda 之间没有区别)。

public class Main {

    static interface ICallback {
        void payload();
    }
    static void measureAnonymousClass() {
        final int arr[] = {0};
        ICallback clb = new ICallback() {
            @Override
            public void payload() {
                arr[0]++;
            }
        };
        clb.payload();
    }
    static void measureLambda() {
        final int arr[] = {0};
        ICallback clb = () -> {
            arr[0]++;
        };
        clb.payload();
    }
    static void runTimed(String message, Runnable act) {
        long start = System.nanoTime();
        for (int i = 0; i < 10_000_000; i++) {
            act.run();
        }
        long end = System.nanoTime();
        System.out.println(message + ":" + (end - start));
    }
    public static void main(String[] args) {
        runTimed("as lambdas", Main::measureLambda);
        runTimed("anonymous class", Main::measureAnonymousClass);
        runTimed("as lambdas", Main::measureLambda);
        runTimed("anonymous class", Main::measureAnonymousClass);
        runTimed("as lambdas", Main::measureLambda);
        runTimed("anonymous class", Main::measureAnonymousClass);
        runTimed("as lambdas", Main::measureLambda);
        runTimed("anonymous class", Main::measureAnonymousClass);
    }
}

两种方法的最后一次运行大约需要 28 秒。


JMH 微基准

我已经跑了the same test with jmh最重要的是,这四种方法花费的时间与等效方法一样多:

void baseline() {
    arr[0]++;
}

换句话说,JIT 内联了匿名类和 lambda,它们花费的时间完全相同。

结果总结:

Benchmark                Mean    Mean error    Units
empty_method             1.104        0.043  nsec/op
baseline                 2.105        0.038  nsec/op
anonymousWithArgs        2.107        0.028  nsec/op
anonymousWithoutArgs     2.120        0.044  nsec/op
lambdaWithArgs           2.116        0.027  nsec/op
lambdaWithoutArgs        2.103        0.017  nsec/op

关于java - java Lambda 与匿名类之间的执行时间差异很大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19001241/

相关文章:

java - 如何将自定义 HTTP header 发送到端点?

performance - 在 postgresql 的列上设置 "NOT NULL"会提高性能吗?

r - 数据框中变量之间的快速成对简单线性回归

python - 在python中递归定义函数

c# - IQueryable<T>.Where() 适合表达式在哪里?

java - 用于业务逻辑和数据库连接的 Junit Spring Boot 单元测试

java - 失败的 Binder 事务正在毁掉整个小部件

java - 如何在 Maven 和 IntelliJ 之间共享 Clover 覆盖率数据

java - String.intern() 的垃圾收集行为

c# - 将此 LINQ 表达式转换为 Lambda