什么时候应该优先使用流而不是传统循环以获得最佳性能?流是否利用分支预测?

26 浏览
0 Comments

什么时候应该优先使用流而不是传统循环以获得最佳性能?流是否利用分支预测?

我刚刚阅读了有关分支预测的内容,并想尝试使用Java 8的Streams来测试它的工作原理。

然而,与传统的循环相比,使用Streams的性能总是更差。

int totalSize = 32768;
int filterValue = 1280;
int[] array = new int[totalSize];
Random rnd = new Random(0);
int loopCount = 10000;
for (int i = 0; i < totalSize; i++) {
    // array[i] = rnd.nextInt() % 2560; // 无序数据
    array[i] = i; // 排序数据
}
long start = System.nanoTime();
long sum = 0;
for (int j = 0; j < loopCount; j++) {
    for (int c = 0; c < totalSize; ++c) {
        sum += array[c] >= filterValue ? array[c] : 0;
    }
}
long total = System.nanoTime() - start;
System.out.printf("条件运算符时间:%d ns,(%f 秒)%n", total, total / Math.pow(10, 9));
start = System.nanoTime();
sum = 0;
for (int j = 0; j < loopCount; j++) {
    for (int c = 0; c < totalSize; ++c) {
        if (array[c] >= filterValue) {
            sum += array[c];
        }
    }
}
total = System.nanoTime() - start;
System.out.printf("分支语句时间:%d ns,(%f 秒)%n", total, total / Math.pow(10, 9));
start = System.nanoTime();
sum = 0;
for (int j = 0; j < loopCount; j++) {
    sum += Arrays.stream(array).filter(value -> value >= filterValue).sum();
}
total = System.nanoTime() - start;
System.out.printf("Streams时间:%d ns,(%f 秒)%n", total, total / Math.pow(10, 9));
start = System.nanoTime();
sum = 0;
for (int j = 0; j < loopCount; j++) {
    sum += Arrays.stream(array).parallel().filter(value -> value >= filterValue).sum();
}
total = System.nanoTime() - start;
System.out.printf("并行Streams时间:%d ns,(%f 秒)%n", total, total / Math.pow(10, 9));

输出:

1. 对于排序数组:

条件运算符时间:294062652 ns,(0.294063 秒)
分支语句时间:272992442 ns,(0.272992 秒)
Streams时间:806579913 ns,(0.806580 秒)
并行Streams时间:2316150852 ns,(2.316151 秒)

2. 对于无序数组:

条件运算符时间:367304250 ns,(0.367304 秒)
分支语句时间:906073542 ns,(0.906074 秒)
Streams时间:1268648265 ns,(1.268648 秒)
并行Streams时间:2420482313 ns,(2.420482 秒)

我尝试使用`List`来运行相同的代码:

将`Arrays.stream(array)`替换为`list.stream()`,

将`array[c]`替换为`list.get(c)`。

输出:

1. 对于排序列表:

条件运算符时间:860514446 ns,(0.860514 秒)
分支语句时间:663458668 ns,(0.663459 秒)
Streams时间:2085657481 ns,(2.085657 秒)
并行Streams时间:5026680680 ns,(5.026681 秒)

2. 对于无序列表:

条件运算符时间:704120976 ns,(0.704121 秒)
分支语句时间:1327838248 ns,(1.327838 秒)
Streams时间:1857880764 ns,(1.857881 秒)
并行Streams时间:2504468688 ns,(2.504469 秒)

我参考了一些博客这个这个,这些博客也指出了Streams的性能问题。

1. 我同意使用Streams编程在某些场景下更加方便,但是当我们在性能上失去优势时,为什么还要使用它们?我是否遗漏了什么?

2. 在哪种情况下Streams的性能与循环相当?是否仅在您定义的函数执行时间很长,而循环性能可以忽略不计的情况下?

3. 在任何情况下,我都没有看到Streams利用了分支预测(我尝试了排序和无序的Streams,但没有用)。与普通的Streams相比,它的性能影响超过了两倍。

0