Java 8中的Lambda Expression是否缩短了执行时间?

时间:2016-12-21 08:26:14

标签: java-8

我是Java 8的新手,对Lambda Expression的范围感到有点困惑。我读了一些文章,表示Lambda Expression减少了执行时间,所以要找到我写的两个程序之后

1)不使用Lambda Expression

import java.util.*;

public class testing_without_lambda
{
  public static void main(String args[])
  {
    long startTime =  System.currentTimeMillis();       
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    for (int number : numbers) 
    {
        System.out.println(number); 
    }   
    long stopTime =  System.currentTimeMillis();
    System.out.print("without lambda:");
    System.out.println(stopTime - startTime);
  }//end main
}

输出:

enter image description here

2)使用Lambda Expression

import java.util.*;
public class testing_with_lambda
{
  public static void main(String args[])
  {  
    long startTime =  System.currentTimeMillis();
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);    
    numbers.forEach((Integer value) -> System.out.println(value));  
    long stopTime =  System.currentTimeMillis();
    System.out.print("with lambda:");
    System.out.print(stopTime - startTime);
  }//end main
}

输出:

enter image description here

这是否意味着Lambda Expression需要更多时间来执行?

3 个答案:

答案 0 :(得分:5)

没有关于“执行时间”的一般说法,因为即使术语“执行时间”并不总是意义相同。当然,没有理由,为什么只使用lambda表达式一般会减少执行时间。

您的代码正在测量代码的初始化时间和它的执行时间,这是公平的,当您考虑该小程序的总执行时间时,但对于现实生活中的应用程序,它没有相关性,因为它们通常运行< em>显着比初始化时间长。

初始化时间的巨大差异是JRE在内部使用Collection API本身的事实,因此在应用程序启动之前,它的类被加载和初始化甚至可能在某种程度上进行优化(所以你不要衡量其成本)。相反,它不使用lambda表达式,因此首次使用lambda表达式将在后台加载并初始化整个框架。

由于您通常对实际应用程序中的代码如何执行感兴趣,因为初始化已经发生,您必须在同一JVM中多次执行代码才能获得更好的图像。但是,允许JVM的优化器处理代码有可能由于其更简单的性质(与现实生活场景相比)而过度优化,并且显示过于乐观的数字。这就是为什么建议使用由专家开发的复杂基准测试工具,而不是创建自己的。即使使用这些工具,您也必须研究他们的文档以了解并避免陷阱。另请参阅How do I write a correct micro-benchmark in Java?

当您将for循环(也称为“外部迭代”)与等效的forEach调用(也称为“内部迭代”)进行比较时,后者确实具有提高效率的潜力,如果由特定的Collection正确实现,但其结果很难预测,因为JVM的优化器擅长消除其他解决方案的缺点。此外,如果您的列表存在,您的列表太小,无法显示出这种差异。

还必须强调的是,这一原则对于lambda表达式并不严格。您还可以通过匿名内部类实现Consumer,在您的情况下,示例遇到第一次初始化成本时,匿名内部类将比lambda表达式更快。<​​/ p>

答案 1 :(得分:3)

Holger's回答之外,我想展示您如何对代码进行基准测试。

您真正测量的是类和系统IO的初始化(即类加载和System.out::println)。

为了摆脱这些,你应该使用像JMH这样的基准框架。此外,您应该使用多个列表或数组大小进行测量。

然后您的代码可能如下所示:

@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@Measurement(iterations = 10, timeUnit = TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

  @Param({ "10", "1000", "10000" })
  private int length;

  private List<Integer> numbers;

  @Setup
  public void setup() {
    Random random = new Random(length);
    numbers = random.ints(length).boxed().collect(Collectors.toList());
  }

  @Benchmark
  public void externalIteration(Blackhole bh) {
    for (Integer number : numbers) {
      bh.consume(number);
    }
  }

  @Benchmark
  public void internalIteration(Blackhole bh) {
    numbers.forEach(bh::consume);
  }
}

结果:

Benchmark                      (length)  Mode  Cnt      Score     Error  Units
MyBenchmark.externalIteration        10  avgt   30     41,002 ±   0,263  ns/op
MyBenchmark.externalIteration      1000  avgt   30   4026,842 ±  71,318  ns/op
MyBenchmark.externalIteration     10000  avgt   30  40423,629 ± 572,055  ns/op
MyBenchmark.internalIteration        10  avgt   30     40,783 ±   0,815  ns/op
MyBenchmark.internalIteration      1000  avgt   30   3888,240 ±  28,790  ns/op
MyBenchmark.internalIteration     10000  avgt   30  41961,320 ± 991,047  ns/op

正如你所看到的,几乎没有区别。

答案 2 :(得分:0)

我不认为lambda表达式代码在执行过程中会更快,它实际上取决于不同的条件。您是否可以指向我阅读的文章,其中lambda表达式的执行时间更快? (由于代码的函数编程风格,在编写代码时当然会被认为更快)

我在当地再次测试,发现了一些奇怪的事情:

  • 我第一次运行没有lambda的代码时,几乎花了相同的时间,而不是像lambda一样。 (49毫秒)
  • 第二次,没有lambda的代码运行得非常快,即1毫秒。
  • 每次使用lambda表达式的代码运行的时间相同,我总共尝试了3-4次。

一般结果:

1 2 3 4 五 6 与lambda:47

1 2 3 4 五 6 没有lambda:1

我认为真的需要一个非常大的数字样本才能真正测试这个,并且多次调用相同的代码来消除JVM上的任何初始化负担。这是一个非常小的测试样本。

相关问题