【问题标题】:Why System.out::println is slower than anonymous class implementation in Java 8?为什么 System.out::println 比 Java 8 中的匿名类实现慢?
【发布时间】:2016-10-09 19:25:11
【问题描述】:

我正在使用一些 Java 8 Stream API。我很困惑地看到以下两种解决方案之间的性能差异,它们只是打印Stream 的内容。

解决方案 1:

int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
start = System.nanoTime();
Arrays.stream(array).forEach(System.out::println);
System.out.println((System.nanoTime() - start) / 1000000.0f);

解决方案 2:

int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
start = System.nanoTime();
Arrays.stream(array).forEach(new IntConsumer() {
    @Override
    public void accept(int value) {
        System.out.println(value);
    }
});
System.out.println((System.nanoTime() - start) / 1000000.0f);

对于执行,Solution 1 大约需要。比 Solution 2 多 5-6 倍。

系统配置:

  • JRE:1.8.0_101 64 bit
  • 操作系统:Windows 10 Home 64-bit
  • 内存:4 GB
  • IDE:Eclipse Mas-1 for Java EE 64-bit

如果有人能解释一下,为什么会有这么大的差异?

JMH 代码:

public class MyBenchmark {

    @Benchmark
    public void solution_0() {
        int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);asdasdas
        }
    }

    @Benchmark
    public void solution_1() {
        int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
        Arrays.stream(array).forEach(new IntConsumer() {
            @Override
            public void accept(int value) {
                System.out.println(value);
            }
        });
    }

    @Benchmark
    public void solution_2() {
        int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
        Arrays.stream(array).forEach(System.out::println);
    }
}

【问题讨论】:

  • 你的测量结果如何?您只使用一次运行吗?微基准测试并非易事。您需要预热,丢弃最高和最低值,考虑平均值和标准偏差等
  • 在说明哪种方法更快之前,您应该尝试了解一些有关 Java 微基准的知识。搜索 Jmh
  • @FedericoPeraltaSchaffner 我已经考虑了 100 次迭代并跳过了前几个元素,因为它们发生了重大变化。我已经运行了相同的程序 4-5 次,每次结果都是一样的。
  • @SergioOteroLopez 我用 Mode.All 尝试了 JMH,但我只在 ns/opops/ns 中得到结果。理想情况下,两种算法都应该相同。我找不到任何东西来获得绝对平均时间。

标签: java performance java-8 java-stream consumer


【解决方案1】:

您正在测量方法引用的实例化,而不是其运行时性能。

在第一次使用方法引用(System.out::println)时,JVM 需要创建一个实现IntConsumer 接口的内部类。当然,这需要时间。虽然这在应用程序生命周期内只执行一次。

在第二种情况下,您自己创建了这样的匿名类。

如果您希望测量方法引用的运行时性能,您必须修改基准测试方法。见“How do I write a correct micro-benchmark in Java?

【讨论】:

  • 嗨..我用 Mode.All 尝试了 JMH,但我只在 ns/opops/ns 中得到结果。理想情况下,两种算法都应该相同。我找不到任何东西来获得绝对平均时间。任何想法都会对我如何检查平均时间有所帮助。
  • @AmberBeriwal ns/op 不是您要查找的平均时间吗?
  • 我理解为纳秒/操作,如果我错了,请纠正我。所以根据我的理解,如果代码 1 包含 100 个操作并在 1000 ns 内完成,那么它将是 10 ns/op。现在,如果代码 2 包含 200 个操作并在 2000 ms 内完成,那么这也是 10 ns/op。但是,在实际代码 2 中需要 2000 毫秒。
  • @AmberBeriwal "operation" - 是 @Benchmark 方法中的内容。您基本上应该将您想要测量的代码放在一个带有@Benchmark 注释的方法中。请使用您提供的 JMH 代码更新问题。
  • @AmberBeriwal 我已经针对不同的问题对 lambda 与匿名类进行了基准测试,您可以将其与它进行比较here is the link
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-07
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-17
相关资源
最近更新 更多