[英]Java8 Lambda performance vs public functions
我已經使用Java8 VS對lambda性能進行了一些演示測試。 Java8公共功能。
案例如下:
我有一個10人(5男5女)的名單。
我想知道哪個女人的年齡在18到25歲之間
現在,當我執行這些步驟數百萬次時,結果將是:
使用ForEach的Lambda:395毫秒(使用JUnit時為396毫秒)
公共函數采用:173毫秒(使用JUnit時為169毫秒)
Lambda with Collect花費了334毫秒(使用JUnit時為335毫秒)
現在我沒想到lambda的執行時間比普通函數長兩倍到六倍。
所以,現在我非常想知道我是否錯過了這里的一些東西。
源代碼可以在這里找到: pastebin.com/BJBk4Tu6
跟進:
結果將是:
與ForEach一起使用的Lambda:59毫秒
公共職能采取:15毫秒
Lambda with Collect:12毫秒
但是,當我嘗試過濾100,000次現有的1.000.000人時,結果如下:
使用ForEach的Lambda:227毫秒
公共職能:134毫秒
帶收集的Lambda:172毫秒
因此,作為最終結論:Lambdas在過濾較大列表時更快,而公共函數(舊方法)在過濾較小列表時更快。
此外,在過濾任何列表時,公共功能更快,無論出於何種目的,您都需要這樣做。
最新代碼: pastebin.com/LcVhgnYv
正如評論中所指出的那樣:你很難從這樣一個簡單而孤立的微基准測試中得出任何結論。
部分引用另一個(不相關的)答案 :
為了正確可靠地測量執行時間,存在幾種選擇。 除了像VisualVM這樣的分析器之外,還有像JMH或Caliper這樣的框架,但不可否認,使用它們可能需要付出一些努力。
對於最簡單的基本手動Java Microbenchmark形式,您必須考慮以下事項:
- 多次運行算法,讓JIT有機會參與進來
- 交替運行算法,而不是一個接一個地運行
- 隨着輸入大小的增加運行算法
- 以某種方式保存並打印計算結果,以防止計算被優化掉
- 考慮垃圾收集器(GC) 可能會導致時序失真
這些只是經驗法則 ,可能仍會有意想不到的結果(有關詳細信息,請參閱上面的鏈接)。 但是這種策略,你通常獲得有關性能的良好指示,並且至少可以看它是否有可能是真的有算法之間顯著的差異。
相關閱讀:
我將這些基本步驟應用到您的程序中。 這是一個MCVE :
注意:其余部分已更新,以響應問題的后續編輯)
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®上的輸出與此類似:
...
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
和default
(公共方法)方法之間的差異即使對於較小的列表也會消失。 對於較大的列表,基於lambda的方法甚至似乎有一點點優勢。
再次強調這一點:這是一個非常簡單的微基准測試,即使這並不一定能說明這些方法在實踐中的表現。 但是,至少可以合理地假設ForEach
和公共方法之間的差異不如初始測試所建議的那么大。 Nevertleless:對於任何在JMH或Caliper中運行此操作的人來說,我都會獲得+1,並對此發表一些進一步的見解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.