nan*_*itv 0 java performance java-8 java-stream
我编写了一个简单的程序来与流的性能进行比较,以查找整数的最大形式列表。令人惊讶的是,我发现“流方式”的性能是“通常方式”的1/10。难道我做错了什么?是否有条件限制哪种Stream方法无效?有人能对此行为做出一个很好的解释吗?
“流方式”花费了80毫秒“流方式”花费了15毫秒请在下面找到代码
public class Performance {
public static void main(String[] args) {
ArrayList<Integer> a = new ArrayList<Integer>();
Random randomGenerator = new Random();
for (int i=0;i<40000;i++){
a.add(randomGenerator.nextInt(40000));
}
long start_s = System.currentTimeMillis( );
Optional<Integer> m1 = a.stream().max(Integer::compare);
long diff_s = System.currentTimeMillis( ) - start_s;
System.out.println(diff_s);
int e = a.size();
Integer m = Integer.MIN_VALUE;
long start = System.currentTimeMillis( );
for(int i=0; i < e; i++)
if(a.get(i) > m) m = a.get(i);
long diff = System.currentTimeMillis( ) - start;
System.out.println(diff);
}
Run Code Online (Sandbox Code Playgroud)
}
是的,对于这种简单的操作,流比较慢。但是您的数字是完全无关的。如果您认为15毫秒的时间可以满足您的任务要求,那么有个好消息:预热流代码可以在0.1-0.2毫秒之内解决此问题,速度提高了70-150倍。
这是肮脏的基准测试:
import java.util.concurrent.TimeUnit;
import java.util.*;
import java.util.stream.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.annotations.*;
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(3)
@State(Scope.Benchmark)
public class StreamTest {
// Stream API is very nice to get random data for tests!
List<Integer> a = new Random().ints(40000, 0, 40000).boxed()
.collect(Collectors.toList());
@Benchmark
public Integer streamList() {
return a.stream().max(Integer::compare).orElse(Integer.MIN_VALUE);
}
@Benchmark
public Integer simpleList() {
int e = a.size();
Integer m = Integer.MIN_VALUE;
for(int i=0; i < e; i++)
if(a.get(i) > m) m = a.get(i);
return m;
}
}
Run Code Online (Sandbox Code Playgroud)
结果是:
Benchmark Mode Cnt Score Error Units
StreamTest.simpleList avgt 30 38.241 ± 0.434 us/op
StreamTest.streamList avgt 30 215.425 ± 32.871 us/op
Run Code Online (Sandbox Code Playgroud)
这是微秒。因此,Stream版本实际上比您的测试要快得多。不过,简单版本甚至更快。因此,如果您使用15 ms没问题,则可以使用您喜欢的这两个版本中的任何一个:两者都将执行得更快。
如果无论如何都希望获得最佳性能,则应摆脱装箱的Integer
对象并使用原始数组:
int[] b = new Random().ints(40000, 0, 40000).toArray();
@Benchmark
public int streamArray() {
return Arrays.stream(b).max().orElse(Integer.MIN_VALUE);
}
@Benchmark
public int simpleArray() {
int e = b.length;
int m = Integer.MIN_VALUE;
for(int i=0; i < e; i++)
if(b[i] > m) m = b[i];
return m;
}
Run Code Online (Sandbox Code Playgroud)
现在两个版本都更快:
Benchmark Mode Cnt Score Error Units
StreamTest.simpleArray avgt 30 10.132 ± 0.193 us/op
StreamTest.streamArray avgt 30 167.435 ± 1.155 us/op
Run Code Online (Sandbox Code Playgroud)
实际上,由于涉及许多在不同时间JIT编译的中间方法,因此流版本的结果可能会有很大的不同,因此,在某些迭代之后,速度可能会沿任何方向改变。
顺便说一句,您的原始问题可以使用良好的旧Collections.max
方法解决,而无需使用Stream API,如下所示:
Integer max = Collections.max(a);
Run Code Online (Sandbox Code Playgroud)
通常,您应该避免测试不能解决实际问题的人工代码。使用人工代码,您将获得人工结果,这些结果通常不会说出真实条件下的API性能。