Java8 Lambda性能与公共函数 [英] Java8 Lambda performance vs public functions

查看:108
本文介绍了Java8 Lambda性能与公共函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Java8 VS进行了一些lambda性能测试。 Java8公共函数。

I've been demo testing a bit with lambda performance using Java8 VS. Java8 public functions.

案例如下:


  1. 我有一个10人(5男5女)的名单。

  1. I have a list existing of 10 people (5 male and 5 female).

我想知道哪个女人的年龄在18到25岁之间

I'd like to know which woman have an age between 18 and 25

现在,当我执行这些步骤数百万次时,结果将是:

Now, when I'm executing these steps a milion times, the results would be:


使用ForEach的Lambda:395 ms(使用JUnit时为396 ms)

Lambda with ForEach took: 395 ms (396 ms using JUnit)

公共函数采取:173 ms(使用JUnit时为169 ms)

Public functions took: 173 ms (169 ms using JUnit)

使用Collect的Lambda:334 ms(使用JUnit时为335 ms)

Lambda with Collect took: 334 ms (335 ms using JUnit)

现在我没想到lambda的执行时间比普通函数长两倍到六倍。

Now I didn't expect the execution time of lambda to be twice up to six times longer then regular functions.

所以,现在我我非常想知道我是否错过了这里的东西。

So, now I'm pretty much wondering whether I've missed something here.

可以在这里找到来源: pastebin.com/BJBk4Tu6

The source can be found here: pastebin.com/BJBk4Tu6

跟进:


  1. 当exp将名单列入1.000.000项目

  2. 并过滤所有年轻的成年女性

结果将是:


使用ForEach的Lambda:59 ms

Lambda with ForEach took: 59 ms

公共函数花了15毫秒

Lambda with Collect花了:12 ms

Lambda with Collect took: 12 ms

然而,当我试图过滤相同的列表1.000.000人100次时,结果将是:

However, when I'm trying to filter the same list existing of 1.000.000 people 100 times, the results would be:


Lambda with ForEach花费了227毫秒

Lambda with ForEach took: 227 ms

公共函数花费:134毫秒

Public functions took: 134 ms

Lambda with Collect花费:172毫秒

Lambda with Collect took: 172 ms

因此,作为最终结论:Lambdas在过滤较大列表时速度更快,而公共函数(旧方法)在过滤较小时更快列表。

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.

此外,在过滤任何列表多次时,公共函数更快,无论出于何种目的,都需要这样做。

Also, public functions are quicker when it comes to filtering any lists multiple times, for whatever purpose you'd require to do that.

最新代码: pastebin.com/LcVhgnYv

推荐答案

正如评论中所指出的:你很难从这样一个简单而孤立的微基准测试中得出任何结论。

As pointed out in the comments: You can hardly draw any conclusion from such a single, simple and isolated microbenchmark run.

部分引用另一个(不相关的)答案


为了正确可靠地测量执行时间,有几个选项。除了 VisualVM 之类的分析器之外,还有像 JMH Caliper ,但不可否认,使用它们可能需要付出一些努力。

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.

对于最简单的形式的一个非常基本的,手动Java Microbenchmark你必须考虑以下几点:

For the simplest form of a very basic, manual Java Microbenchmark you have to consider the following:


  • 多次运行算法,让JIT有机会进入

  • 交替运行算法而不是一个接一个地运行算法

  • 运行增加输入大小的算法

  • 以某种方式保存

  • 考虑时间可能被垃圾收集器(GC)扭曲
  • 并打印计算结果,以防止计算被优化掉>
  • Run the algorithms multiple times, to give the JIT a chance to kick in
  • 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)

这些只是经验法则,可能仍有意外结果(有关详细信息,请参阅上面的链接)。但是通过这种策略,您通常可以获得关于性能的良好指示,并且至少可以看出真正是否可能是算法之间的显着差异。

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.

相关阅读:

  • How do I write a correct micro-benchmark in Java?
  • Java theory and practice: Anatomy of a flawed microbenchmark
  • HotSpot Internals

我将这些基本步骤应用到您的程序中。这是一个 MCVE

I applied these basic steps to your program. Here is an 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;
    }
}

MyMachine®上的输出与这个:

The output on My Machine® is along the lines of this:

    ...
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

你可以看到 ForEach 默认(公共方法)方法即使对于较小的列表也会消失。对于较大的列表,基于lambda的方法甚至似乎有一点点优势。

You can see that the difference between the ForEach and the default (public methods) approach is vanishing even for smaller lists. For larger lists, there even seems to be a slight advantage for the lambda-based approaches.

再次强调这一点:这是一个非常简单的微基准测试,即使这并不一定能说明这些方法在实践中的表现。但是,至少可以合理地假设 ForEach 与公共方法之间的差异不如初始测试所建议的那么大。 Nevertleless:对于任何在JMH或Caliper中运行此操作的人来说,给我+1,并对此发表一些进一步的见解。

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. Nevertleless: +1 from me for anybody who runs this in JMH or Caliper and posts some further insights about this.

这篇关于Java8 Lambda性能与公共函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆