草庐IT

Java8 Lambda 性能与公共(public)函数

coder 2024-03-28 原文

我一直在使用 Java8 VS 对 lambda 性能进行一些演示测试。 Java8 公共(public)函数。

案例如下:

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

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

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

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

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

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

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

所以,现在我很想知道我是否在这里遗漏了什么。

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


跟进:

  1. 将列表扩展到 1.000.000 项时
  2. 并对所有年轻成年女性进行一次过滤

结果是:

Lambda with ForEach took: 59 ms

Public functions took: 15 ms

Lambda with Collect took: 12 ms

但是,当我尝试过滤同一个包含 1.000.000 人的列表 100 次时,结果将是:

Lambda with ForEach took: 227 ms

Public functions took: 134 ms

Lambda with Collect took: 172 ms

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

此外,无论您出于何种目的需要多次过滤任何列表,公共(public)函数都会更快。

最新代码:pastebin.com/LcVhgnYv

最佳答案

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

部分引用自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.

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

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

Related reading:

我将这些基本步骤应用于您的程序。这是一个 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;
    }
}

My Machine® 上的输出是这样的:

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

您可以看到 ForEachdefault(公共(public)方法)方法之间的区别正在消失,即使对于较小的列表也是如此。对于更大的列表,基于 lambda 的方法似乎甚至有一点优势。

再次强调:这是一个非常简单的微基准测试,即使这样也不一定能说明这些方法在实践中的性能。但是,至少可以合理地假设 ForEach 和公共(public)方法之间的差异没有初始测试建议的那么大。 Nevertleless:我为在 JMH 或 Caliper 中运行此程序并发布了一些关于此的进一步见解的任何人 +1。

关于Java8 Lambda 性能与公共(public)函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26252672/

有关Java8 Lambda 性能与公共(public)函数的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  3. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  4. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  5. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  6. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  7. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  8. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  9. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  10. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

随机推荐