Chi*_*pen 2 java performance bytecode java-8 java-stream
据我所知,如果我有一个带有两个过滤器的流,它们将与字节码中的&&结合使用.
例如
IntStream.range(1,10)
.filter(i -> i % 2 == 0)
.filter(i -> i % 3 == 0)
.sum();
Run Code Online (Sandbox Code Playgroud)
会像i%2 == 0 && i%3 == 0.
偷看会影响这个吗?
如果你在第一个文件管理器后偷看你得到2468,如果你偷看第二个,你只得到6(当然).
但如果你偷看这两个地方
IntStream.range(1,10)
.filter(integer -> integer % 2 == 0)
.peek(i-> System.out.print(i))
.filter(integer -> integer % 3 == 0)
.peek(i-> System.out.print(i))
.sum();
Run Code Online (Sandbox Code Playgroud)
你得到24668.
我的假设是,这必然意味着操作在某种程度上是由于窥视调用而分开的.就像是
if(i%2==0)
peek
if(i%3==0)
Run Code Online (Sandbox Code Playgroud)
这是真的,如果是这样会影响性能(我认为不会).
该StreamAPI是一个普通的Java API,你可以看到自己.它的filter方法接收一个任意的Predicate实例,无论是通过lambda表达式还是普通的class(或enum命名所有可能性).
如果您filter随后调用了两次,则底层实现可以通过调用将它们连接到单个过滤器,Predicate.and但是在通过lambda表达式实现的谓词的情况下它是否有效.
与自定义Predicate实现不同,自定义实现可以覆盖and方法并在它们识别第二个Predicate实现的情况下提供优化的东西,为lambda表达式生成的类不会覆盖任何default方法,而只是一个abstract函数方法,这里Predicate.test,所以在这种情况下,调用and将得到该default方法返回的内容,一个新的Predicate,它包含对两个源谓词的引用并将它们组合起来,就像一个不使用的Stream实现Predicate.and一样.
因此,这些可能的实现之间没有实质性的差异,如果你插入另一个像Consumer传递给peek中间的动作则没有.当然,它现在比没有这个动作比较多,所以它有一个性能的影响,但不是关于谓词.
但是你的一般误解似乎是你认为以下两者之间存在重大差异:
for(int i=1; i<10; i++) {
if(i%2==0 && i%3==0)
System.out.print(i);
}
Run Code Online (Sandbox Code Playgroud)
和
for(int i=1; i<10; i++) {
if(i%2==0) {
System.out.print(i);
if(i%3==0)
System.out.print(i);
}
}
Run Code Online (Sandbox Code Playgroud)
看看编译方法的字节码:
// first variant second variant
0: iconst_1 0: iconst_1
1: istore_1 1: istore_1
2: iload_1 2: iload_1
3: bipush 10 3: bipush 10
5: if_icmpge 33 5: if_icmpge 40
8: iload_1 8: iload_1
9: iconst_2 9: iconst_2
10: irem 10: irem
11: ifne 27 11: ifne 34
14: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_1
18: invokevirtual #3 // Method java/io/PrintStream.print:(I)V
14: iload_1 21: iload_1
15: iconst_3 22: iconst_3
16: irem 23: irem
17: ifne 27 24: ifne 34
20: getstatic #2 27: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
23: iload_1 30: iload_1
24: invokevirtual #3 31: invokevirtual #3 // Method java/io/PrintStream.print:(I)V
27: iinc 1, 1 34: iinc 1, 1
30: goto 2 37: goto 2
33: return 40: return
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,插入print语句恰好会导致插入print语句,仅此而已.或者,换句话说,&&运算符不是与两个嵌套if语句不同的神奇融合.两者在语义和字节代码中完全相同.
这同样适用于Stream API用法,尽管如此,代码将更复杂,因为条件表达式表示为Predicate实例,插入的语句是Consumers.但在最好的情况下,HotSpot优化器将为Stream变体生成与循环变体完全相同的优化本机代码.
| 归档时间: |
|
| 查看次数: |
161 次 |
| 最近记录: |