我使用最后的示例代码来衡量性能。
如果我调用 checkPerformanceResult 方法并将参数 numberOfTimes 设置为 100,则并行流的性能显着优于顺序流(sequential=346,parallel=78)。
如果我将参数设置为 1000,顺序流的性能明显优于并行流(sequential=3239,parallel=9337)。
我运行了很多次,结果都是一样的。
有人可以向我解释一下这种行为以及这里到底发生了什么吗?
public class ParallelStreamExample {
public static long checkPerformanceResult(Supplier<Integer> s, int numberOfTimes) {
long startTime = System.currentTimeMillis();
for(int i = 0; i < numberOfTimes; i++) {
s.get();
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
public static int sumSequentialStreamThread() {
IntStream.rangeClosed(1, 10000000).sum();
return 0;
}
public static int sumParallelStreamThread() {
IntStream.rangeClosed(1, 10000000)
.parallel().sum();
return 0;
}
public static void main(String[] args) {
System.out.println(checkPerformanceResult(ParallelStreamExample::sumSequentialStreamThread, 1000));
System.out.println("break");
System.out.println(checkPerformanceResult(ParallelStreamExample::sumParallelStreamThread, 1000));
}
}
最佳答案
使用线程并不总是能让代码运行得更快
当使用几个线程时,总是存在管理每个线程的开销(通过操作系统为每个线程分配 CPU 时间,管理在上下文切换等情况下需要运行的下一行代码......)
在本例中
在sumParallelStreamThread
中创建的每个线程在内存操作中都执行非常简单的操作(调用返回数字的函数)。
因此,sumSequentialStreamThread
和 sumParallelStreamThread
之间的区别在于,在 sumParallelStreamThread
中,每个简单操作都有创建线程并运行它的开销(假设后台没有发生任何线程优化)。
和 sumSequentialStreamThread
执行相同的操作,而无需管理所有线程的开销,这就是它运行速度更快的原因。
何时使用线程
使用线程最常见的用例是当您需要执行大量 I/O 任务时。
什么被视为 I/O 任务?
这取决于几个因素,你可以找到关于它的辩论here 。 但我认为一般来说人们会同意向某个地方发出 HTTP 请求或执行数据库查询可以被视为 I/O 操作。
为什么更合适?
因为 I/O 操作通常需要一段时间来等待与之相关的响应。 例如,在查询数据库时,执行查询的线程将等待数据库返回响应(即使不到半秒),而该线程正在等待另一个线程可以执行其他操作,这就是我们可以获得性能的地方。
我发现通常在不同线程中运行仅涉及 RAM 内存和 CPU 操作的任务会使代码运行速度比单个线程慢。
基准讨论
关于评论中的基准评论,我不确定它们是否正确,但在这种情况下,我会根据任何分析工具(或只是使用它开始)仔细检查我的基准,例如 JProfiler或YoutKit它们通常非常准确。
关于java - 如果增加迭代次数,顺序流比并行流更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58142949/