一、粉丝的反馈
问:stream比for循环慢5倍,用这个是为了啥? 答:互联网是一个新闻泛滥的时代,三人成虎,以假乱真的事情时候发生。作为一个技术开发者,要自己去动手去做,不要人云亦云。
的确,这位粉丝说的这篇文章我也看过,我就不贴地址了,也没必要给他带流量。怎么说呢?就是一个不懂得测试的、不入流开发工程师做的性能测试,给出了一个危言耸听的结论。
二、所有性能测试结论都是片面的
性能测试是必要的,但针对性能测试的结果,永远要持怀疑态度。为什么这么说?
性能测试脱离业务场景就是片面的性能测试。你能覆盖所有的业务场景么? 性能测试脱离硬件环境就是片面的性能测试。你能覆盖所有的硬件环境么? 性能测试脱离开发人员的知识面就是片面的性能测试。你能覆盖各种开发人员奇奇怪怪的代码么?所以,我从来不相信网上的任何性能测试的文章。凡是我自己的从事的业务场景,我都要在接近生产环境的机器上自己测试一遍。 所有性能测试结论都是片面的,只有你生产环境下的运行结果才是真的。
三、动手测试Stream的性能
3.1.环境
windows10 、16G内存、i7-7700HQ 2.8HZ 、64位操作系统、JDK 1.8.0_171
3.2.测试用例与测试结论
我们在上一节,已经讲过:
针对不同的数据结构,Stream流的执行效率是不一样的 针对不同的数据源,Stream流的执行效率也是不一样的所以记住笔者的话:所有性能测试结论都是片面的,你要自己动手做,相信你自己的代码和你的环境下的测试!我的测试结果仅仅代表我自己的测试用例和测试数据结构!
3.2.1.测试用例一
测试用例:5亿个int随机数,求最小值 测试结论(测试代码见后文):
使用普通for循环,执行效率是Stream串行流的2倍。也就是说普通for循环性能更好。 Stream并行流计算是普通for循环执行效率的4-5倍。 Stream并行流计算 > 普通for循环 > Stream串行流计算
3.2.2测试用例二
测试用例:长度为10的1000000随机字符串,求最小值 测试结论(测试代码见后文):
普通for循环执行效率与Stream串行流不相上下 Stream并行流的执行效率远高于普通for循环 Stream并行流计算 > 普通for循环 = Stream串行流计算
3.2.3测试用例三
测试用例:10个用户,每人200个订单。按用户统计订单的总价。 测试结论(测试代码见后文):
Stream并行流的执行效率远高于普通for循环 Stream串行流的执行效率大于等于普通for循环 Stream并行流计算 > Stream串行流计算 >= 普通for循环
四、最终测试结论
对于简单的数字(list-Int)遍历,普通for循环效率的确比Stream串行流执行效率高(1.5-2.5倍)。但是Stream流可以利用并行执行的方式发挥CPU的多核优势,因此并行流计算执行效率高于for循环。
对于list-Object类型的数据遍历,普通for循环和Stream串行流比也没有任何优势可言,更不用提Stream并行流计算。
虽然在不同的场景、不同的数据结构、不同的硬件环境下。Stream流与for循环性能测试结果差异较大,甚至发生逆转。但是总体上而言:
Stream并行流计算 >> 普通for循环 ~= Stream串行流计算 (之所以用两个大于号,你细品) 数据容量越大,Stream流的执行效率越高。 Stream并行流计算通常能够比较好的利用CPU的多核优势。CPU核心越多,Stream并行流计算效率越高。stream比for循环慢5倍?也许吧,单核CPU、串行Stream的int类型数据遍历?我没试过这种场景,但是我知道这不是应用系统的核心场景。看了十几篇测试博文,和我的测试结果。我的结论是: 在大多数的核心业务场景下及常用数据结构下,Stream的执行效率比for循环更高。 毕竟我们的业务中通常是实实在在的实体对象,没事谁总对 List < Int > 类型进行遍历?谁的生产服务器是单核?。
五、测试代码
<dependency>
<groupId> com.github.houbb </groupId>
<artifactId> junitperf </artifactId>
<version> 2.0.0 </version>
</dependency>
测试用例一:
import com . github . houbb . junitperf . core . annotation . JunitPerfConfig ;
import com . github . houbb . junitperf . core . report . impl . HtmlReporter ;
import org . junit . jupiter . api . BeforeAll ;
import java . util . Arrays ;
import java . util . Random ;
public class StreamIntTest {
public static int [] arr ;
@BeforeAll
public static void init () {
arr = new int [ 500000000 ]; //5亿个随机Int
randomInt ( arr );
}
@JunitPerfConfig ( warmUp = 1000 , reporter = { HtmlReporter . class })
public void testIntFor () {
minIntFor ( arr );
}
@JunitPerfConfig ( warmUp = 1000 , reporter = { HtmlReporter . class })
public void testIntParallelStream () {
minIntParallelStream ( arr );
}
@JunitPerfConfig ( warmUp = 1000 , reporter = { HtmlReporter . class })
public void testIntStream () {
minIntStream ( arr );
}
private int minIntStream ( int [] arr ) {
return Arrays . stream ( arr ). min (). getAsInt ();
}
private int minIntParallelStream ( int [] arr ) {
return Arrays . stream ( arr ). parallel (). min (). getAsInt ();
}
private int minIntFor ( int [] arr ) {
int min = Integer . MAX_VALUE ;
for ( int anArr : arr ) {
if ( anArr < min ) {
min = anArr ;
}
}
return min ;
}
private static void randomInt ( int [] arr ) {
Random r = new Random ();
for ( int i = 0 ; i < arr . length ; i ++) {
arr [ i ] = r . nextInt ();
}
}
}
测试用例二:
import com . github . houbb . junitperf . core . annotation . JunitPerfConfig ;
import com . github . houbb . junitperf . core . report . impl . HtmlReporter ;
import org . junit . jupiter . api . BeforeAll ;
import java . util . ArrayList ;
import java . util . Random ;
public class StreamStringTest {
public static ArrayList < String > list ;
@BeforeAll
public static void init () {
list = randomStringList ( 1000000 );
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void testMinStringForLoop (){
String minStr = null ;
boolean first = true ;
for ( String str : list ){
if ( first ){
first = false ;
minStr = str ;
}
if ( minStr . compareTo ( str )> 0 ){
minStr = str ;
}
}
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void textMinStringStream (){
list . stream (). min ( String :: compareTo ). get ();
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void testMinStringParallelStream (){
list . stream (). parallel (). min ( String :: compareTo ). get ();
}
private static ArrayList < String > randomStringList ( int listLength ){
ArrayList < String > list = new ArrayList <>( listLength );
Random rand = new Random ();
int strLength = 10 ;
StringBuilder buf = new StringBuilder ( strLength );
for ( int i = 0 ; i < listLength ; i ++){
buf . delete ( 0 , buf . length ());
for ( int j = 0 ; j < strLength ; j ++){
buf . append (( char )( 'a' + rand . nextInt ( 26 )));
}
list . add ( buf . toString ());
}
return list ;
}
}
测试用例三:
import com . github . houbb . junitperf . core . annotation . JunitPerfConfig ;
import com . github . houbb . junitperf . core . report . impl . HtmlReporter ;
import org . junit . jupiter . api . BeforeAll ;
import java . util .*;
import java . util . stream . Collectors ;
public class StreamObjectTest {
public static List < Order > orders ;
@BeforeAll
public static void init () {
orders = Order . genOrders ( 10 );
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void testSumOrderForLoop (){
Map < String , Double > map = new HashMap <>();
for ( Order od : orders ){
String userName = od . getUserName ();
Double v ;
if (( v = map . get ( userName )) != null ){
map . put ( userName , v + od . getPrice ());
} else {
map . put ( userName , od . getPrice ());
}
}
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void testSumOrderStream (){
orders . stream (). collect (
Collectors . groupingBy ( Order :: getUserName ,
Collectors . summingDouble ( Order :: getPrice )));
}
@JunitPerfConfig ( duration = 10000 , warmUp = 1000 , reporter = { HtmlReporter . class })
public void testSumOrderParallelStream (){
orders . parallelStream (). collect (
Collectors . groupingBy ( Order :: getUserName ,
Collectors . summingDouble ( Order :: getPrice )));
}
}
class Order {
private String userName ;
private double price ;
private long timestamp ;
public Order ( String userName , double price , long timestamp ) {
this . userName = userName ;
this . price = price ;
this . timestamp = timestamp ;
}
public String getUserName () {
return userName ;
}
public double getPrice () {
return price ;
}
public long getTimestamp () {
return timestamp ;
}
public static List < Order > genOrders ( int listLength ){
ArrayList < Order > list = new ArrayList <>( listLength );
Random rand = new Random ();
int users = listLength / 200 ; // 200 orders per user
users = users == 0 ? listLength : users ;
ArrayList < String > userNames = new ArrayList <>( users );
for ( int i = 0 ; i < users ; i ++){
userNames . add ( UUID . randomUUID (). toString ());
}
for ( int i = 0 ; i < listLength ; i ++){
double price = rand . nextInt ( 1000 );
String userName = userNames . get ( rand . nextInt ( users ));
list . add ( new Order ( userName , price , System . nanoTime ()));
}
return list ;
}
@Override
public String toString (){
return userName + "::" + price ;
}
}
以上就是java理论基础Stream性能论证测试示例的详细内容,更多关于java Stream性能测试的资料请关注其它相关文章!
原文链接:https://zimug.blog.csdn.net/article/details/106936146
查看更多关于java理论基础Stream性能论证测试示例的详细内容...