Java 为什么IntStream.range(0,100000).parallel().foreach比普通循环花费更长的时间

Java 为什么IntStream.range(0,100000).parallel().foreach比普通循环花费更长的时间,java,benchmarking,java-stream,Java,Benchmarking,Java Stream,我刚刚开始学习Java中的Streams和parallel,我想知道为什么正常的for循环在向数组添加项时比IntStreamparallel花费的时间要少 package parallel; import java.util.stream.IntStream; public class Parallel { public static void main(String[] args) { final int[] intArray = new int[100000

我刚刚开始学习Java中的Streams和parallel,我想知道为什么正常的for循环在向数组添加项时比
IntStream
parallel花费的时间要少

package parallel;

import java.util.stream.IntStream;

public class Parallel {

    public static void main(String[] args) {
         final int[] intArray = new int[100000];
        long startTime = System.currentTimeMillis(); 
        IntStream.range(0, 100000).parallel().forEach(i ->  intArray[i]=i);
        long endTime = System.currentTimeMillis();
        System.out.println("Parallel time: " + (endTime-startTime));
        final int[] intArray2 = new int[100000];
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        startTime = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++){
            intArray2[i] = i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("Non parallel time: " + (endTime-startTime));
    }
}
包并行;
导入java.util.stream.IntStream;
公共类并行{
公共静态void main(字符串[]args){
最终整数[]整数=新整数[100000];
long startTime=System.currentTimeMillis();
IntStream.range(0,100000).parallel().forEach(i->intArray[i]=i);
long-endTime=System.currentTimeMillis();
System.out.println(“并行时间:+(结束时间开始时间));
最终整数[]整数2=新整数[100000];
试一试{
睡眠(100);
}捕捉(中断异常e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
startTime=System.currentTimeMillis();
对于(int i=0;i<100000;i++){
intArray2[i]=i;
}
endTime=System.currentTimeMillis();
System.out.println(“非并行时间:+(结束时间开始时间));
}
}
得到这样的结果

平行时间:110


非并行时间:7

对每个元素执行的操作非常简单,它只是一个赋值,速度非常快。在并行版本中,启动处理操作的多个线程会带来大量开销。仅此一项就可能比非并行应用时非常简单的操作所需的时间更长

此外,在非并行版本中,值以非常线性的方式写入阵列,该阵列的CPU体系结构已经有一段时间进行了单威胁/单核优化,编译器和中间编译器(它们将C等代码转换为汇编代码)也是如此。但在并行版本中,当每个线程尝试写入同一个数组时(虽然位于不同位置,但可能仍在同一缓存线上),您可能会遇到冲突;当多个线程访问数组的不同部分时,您也可能会遇到缓存未命中,这会降低速度


使用更昂贵的操作时,与总成本相比,并行版本的开销会更小,这最终会导致比非并行版本执行更快。

首先,众所周知,基准测试很难准确执行。微基准比无用的还要糟糕。我要指出的是,您的数组已经构建好,您循环并设置了一个值,而并行foreach不是以预先初始化的值开始的。有两个不同的数组正在填充(
intArray
intArray2
),因此这两种情况都是从“干净”数组开始的。问题是您的测量方法,句号。这种测量方法的最大问题是,它打印出一个数字,人们试图将其解释为有意义的数字。事实并非如此。但我怀疑这会造成如此大的差异——有缺陷的基准测试可能也是解释的一部分。当然,它应该在一次像样的JVM预热后执行很多次,然后进行平均,否则结果中会有太多随机噪声。但是,即使有正确的设置,我的猜测是,使用上述代码,并行的速度仍然会较慢(仅根据个人经验,正确的基准将证明我是对的还是错的;)。您建议
parallel()。forEach
将在交错i值的线程之间分配工作,例如,4个线程中的每个线程从不同的起点执行
i+=4
。这将使接口对使用数组执行任何操作都非常不利;当然,一个合理的实现会将范围分解为
i
值的连续子范围,因此每个线程只接触一些缓存线。线程启动开销和有缺陷的基准测试(JVM没有预热、CPU频率或数组页面错误)仍然足以解释仅超过400kB的简单memset循环的性能差异。