简体   繁体   English

Java8 Lambda性能与公共功能

[英]Java8 Lambda performance vs public functions

I've been demo testing a bit with lambda performance using Java8 VS. 我已经使用Java8 VS对lambda性能进行了一些演示测试。 Java8 public functions. Java8公共功能。

The case is as the following: 案例如下:

  1. I have a list existing of 10 people (5 male and 5 female). 我有一个10人(5男5女)的名单。

  2. I'd like to know which woman have an age between 18 and 25 我想知道哪个女人的年龄在18到25岁之间

Now, when I'm executing these steps a milion times, the results would be: 现在,当我执行这些步骤数百万次时,结果将是:

Lambda with ForEach took: 395 ms (396 ms using JUnit) 使用ForEach的Lambda:395毫秒(使用JUnit时为396毫秒)

Public functions took: 173 ms (169 ms using JUnit) 公共函数采用:173毫秒(使用JUnit时为169毫秒)

Lambda with Collect took: 334 ms (335 ms using JUnit) Lambda with Collect花费了334毫秒(使用JUnit时为335毫秒)

Now I didn't expect the execution time of lambda to be twice up to six times longer then regular functions. 现在我没想到lambda的执行时间比普通函数长两倍到六倍。

So, now I'm pretty much wondering whether I've missed something here. 所以,现在我非常想知道我是否错过了这里的一些东西。

The source can be found here: pastebin.com/BJBk4Tu6 源代码可以在这里找到: pastebin.com/BJBk4Tu6


Follow up: 跟进:

  1. When expanding the list to 1.000.000 items 将列表扩展为1.000.000项时
  2. and filtering all young adult woman once 并过滤所有年轻的成年女性一次

The results would be: 结果将是:

Lambda with ForEach took: 59 ms 与ForEach一起使用的Lambda:59毫秒

Public functions took: 15 ms 公共职能采取:15毫秒

Lambda with Collect took: 12 ms Lambda with Collect:12毫秒

However, when I'm trying to filter the same list existing of 1.000.000 people 100 times, the results would be: 但是,当我尝试过滤100,000次现有的1.000.000人时,结果如下:

Lambda with ForEach took: 227 ms 使用ForEach的Lambda:227毫秒

Public functions took: 134 ms 公共职能:134毫秒

Lambda with Collect took: 172 ms 带收集的Lambda:172毫秒

So, as a final conclusion: Lambdas are quicker when it comes to filtering larger lists while public function (the old way) are quicker at filtering smaller lists. 因此,作为最终结论:Lambdas在过滤较大列表时更快,而公共函数(旧方法)在过滤较小列表时更快。

Also, public functions are quicker when it comes to filtering any lists multiple times, for whatever purpose you'd require to do that. 此外,在过滤任何列表时,公共功能更快,无论出于何种目的,您都需要这样做。

Latest code: pastebin.com/LcVhgnYv 最新代码: pastebin.com/LcVhgnYv

As pointed out in the comments: You can hardly draw any conclusion from such a single, simple and isolated microbenchmark run. 正如评论中所指出的那样:你很难从这样一个简单而孤立的微基准测试中得出任何结论。

Partially quoting from another (otherwise unrelated) answer : 部分引用另一个(不相关的)答案

In order to properly and reliably measure execution times, there exist several options. 为了正确可靠地测量执行时间,存在几种选择。 Apart from a profiler, like VisualVM , there are frameworks like JMH or Caliper , but admittedly, using them may be some effort. 除了像VisualVM这样的分析器之外,还有像JMHCaliper这样的框架,但不可否认,使用它们可能需要付出一些努力。

For the simplest form of a very basic, manual Java Microbenchmark you have to consider the following: 对于最简单的基本手动Java Microbenchmark形式,您必须考虑以下事项:

  • Run the algorithms multiple times, to give the JIT a chance to kick in 多次运行算法,让JIT有机会参与进来
  • Run the algorithms alternatingly and not only one after the other 交替运行算法,而不是一个接一个地运行
  • Run the algorithms with increasing input size 随着输入大小的增加运行算法
  • Somehow save and print the results of the computation, to prevent the computation from being optimized away 以某种方式保存并打印计算结果,以防止计算被优化掉
  • Consider that timings may be distorted by the garbage collector (GC) 考虑垃圾收集器(GC) 可能会导致时序失真

These are only rules of thumb , and there may still be unexpected results (refer to the links above for more details). 这些只是经验法则 ,可能仍会有意想不到的结果(有关详细信息,请参阅上面的链接)。 But with this strategy, you usually obtain a good indication about the performance, and at least can see whether it's likely that there really are significant differences between the algorithms. 但是这种策略,你通常获得有关性能的良好指示,并且至少可以看它是否有可能是真的有算法之间显著的差异。

Related reading: 相关阅读:

I applied these basic steps to your program. 我将这些基本步骤应用到您的程序中。 Here is an MCVE : 这是一个MCVE

NOTE: The remaining part was updated in response to the follow-up edit of the question) 注意:其余部分已更新,以响应问题的后续编辑)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

class Person {
    public static final int MALE = 0;
    public static final int FEMALE = 1;
    private final String name;
    private final int sex;
    private final int age;

    public Person(String name, int sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public int getAge() {
        return age;
    }
}

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    private List<Person> people;

    public Main() {

        for (int size=10; size<=1000000; size*=10) {

            Random r = new Random(0);
            people = new ArrayList<Person>();
            for (int i = 0; i < size; i++) {
                int s = r.nextInt(2);
                int a = 25 + r.nextInt(20);
                people.add(new Person("p" + i, s, a));
            }

            int min = 10000000 / size;
            int max = 10 * min;
            for (int n = min; n <= max; n += min) {
                lambdaMethodUsingForEach(n);
                lambdaMethodUsingCollect(n);
                defaultMethod(n);
            }
        }
    }

    public void lambdaMethodUsingForEach(int n) {
        List<Person> lambdaOutput = new ArrayList<Person>();
        long lambdaStart = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            lambdaOutput.addAll(getFemaleYoungAdultsUsingLambdaUsingForEach());
        }
        System.out.printf("List size: %10d, runs: %10d, result: %10d, ForEach took: " +
            (System.currentTimeMillis() - lambdaStart) + " ms\n",
            people.size(), n, lambdaOutput.size());
    }

    public void lambdaMethodUsingCollect(int n) {
        List<Person> lambdaOutput = new ArrayList<Person>();
        long lambdaStart = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            lambdaOutput.addAll(getFemaleYoungAdultsUsingLambdaUsingCollect());
        }
        System.out.printf("List size: %10d, runs: %10d, result: %10d, collect took: " +
            (System.currentTimeMillis() - lambdaStart) + " ms\n",
            people.size(), n, lambdaOutput.size());
    }

    public void defaultMethod(int n) {
        List<Person> defaultOutput = new ArrayList<Person>();
        long defaultStart = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            defaultOutput.addAll(getFemaleYoungAdultsUsingFunctions());
        }
        System.out.printf("List size: %10d, runs: %10d, result: %10d, default took: " +
            (System.currentTimeMillis() - defaultStart) + " ms\n",
            people.size(), n, defaultOutput.size());
    }

    public List<Person> getFemaleYoungAdultsUsingLambdaUsingForEach() {
        List<Person> people = new ArrayList<Person>();
        this.people.stream().filter(
                (p) -> p.getSex() == Person.FEMALE &&
                p.getAge() >= 18 &&
                p.getAge() <= 25).forEach(people::add);
        return people;
    }

    public List<Person> getFemaleYoungAdultsUsingLambdaUsingCollect() {
        return this.people.stream().filter(
                (p) -> p.getSex() == Person.FEMALE &&
                p.getAge() >= 18 &&
                p.getAge() <= 25).collect(Collectors.toList());
    }

    public List<Person> getFemaleYoungAdultsUsingFunctions() {
        List<Person> people = new ArrayList<Person>();
        for (Person p : this.people) {
            if (p.getSex() == Person.FEMALE && p.getAge() >= 18 && p.getAge() <= 25) {
                people.add(p);
            }
        }
        return people;
    }
}

The output on My Machine® is along the lines of this: MyMachine®上的输出与此类似:

    ...
List size:       10, runs:   10000000, result:   10000000, ForEach took: 1482 ms
List size:       10, runs:   10000000, result:   10000000, collect took: 2014 ms
List size:       10, runs:   10000000, result:   10000000, default took: 1013 ms
...
List size:      100, runs:    1000000, result:    3000000, ForEach took: 664 ms
List size:      100, runs:    1000000, result:    3000000, collect took: 515 ms
List size:      100, runs:    1000000, result:    3000000, default took: 441 ms
...
List size:     1000, runs:     100000, result:    2300000, ForEach took: 778 ms
List size:     1000, runs:     100000, result:    2300000, collect took: 721 ms
List size:     1000, runs:     100000, result:    2300000, default took: 841 ms
...
List size:    10000, runs:      10000, result:    2450000, ForEach took: 970 ms
List size:    10000, runs:      10000, result:    2450000, collect took: 971 ms
List size:    10000, runs:      10000, result:    2450000, default took: 1119 ms
...
List size:   100000, runs:       1000, result:    2536000, ForEach took: 976 ms
List size:   100000, runs:       1000, result:    2536000, collect took: 1057 ms
List size:   100000, runs:       1000, result:    2536000, default took: 1109 ms
...
List size:  1000000, runs:        100, result:    2488600, ForEach took: 1323 ms
List size:  1000000, runs:        100, result:    2488600, collect took: 1305 ms
List size:  1000000, runs:        100, result:    2488600, default took: 1422 ms

You can see that the difference between the ForEach and the default (public methods) approach is vanishing even for smaller lists. 您可以看到ForEachdefault (公共方法)方法之间的差异即使对于较小的列表也会消失。 For larger lists, there even seems to be a slight advantage for the lambda-based approaches. 对于较大的列表,基于lambda的方法甚至似乎有一点点优势。

To emphasize this again: This is a very simple microbenchmark, and even this does not necessarily tell much about the performance of these approaches in practice. 再次强调这一点:这是一个非常简单的微基准测试,即使这并不一定能说明这些方法在实践中的表现。 However, it is at least reasonable to assume that the difference between the ForEach and the public methods is not as large as suggested by your initial test. 但是,至少可以合理地假设ForEach和公共方法之间的差异不如初始测试所建议的那么大。 Nevertleless: +1 from me for anybody who runs this in JMH or Caliper and posts some further insights about this. Nevertleless:对于任何在JMH或Caliper中运行此操作的人来说,我都会获得+1,并对此发表一些进一步的见解。

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

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