当前位置: 首页 > 知识库问答 >
问题:

大约。用于比较Java中的顺序V/S并行速度。为什么.parallel()比较慢?

訾雅畅
2023-03-14

我正在使用一个非常著名的圆环近似值的例子。我选取单位正方形((0,0)到(1,1))中的随机点,看看有多少随机点落在单位圆的区域内。分数应为?/4的值。

public class PIEstimation {
    final static int NUM_SAMPLES = 100000000;

    public static void main(String[] args) {

    sequentialVersion();
    parallelVersion();
    System.out.println("               Real PI:= " + Math.PI);
    }

    public static void sequentialVersion() {
    final long start = System.nanoTime();

    final long count = LongStream
        .rangeClosed(1, NUM_SAMPLES)
        .filter(e -> {
                double x = Math.random();
                double y = Math.random();
                return x * x + y * y < 1;
    }).count();

    final long duration = ((System.nanoTime() - start) / 1_000_000);

    System.out.println("Sequential Version: PI ~ " + 4.0 * (count / (double) NUM_SAMPLES) + " calculated in "
        + duration + " msecs");
    }

    public static void parallelVersion() {
    final long start = System.nanoTime();

    final long count = LongStream
        .rangeClosed(1, NUM_SAMPLES)
        .parallel()
        .filter(e -> {
                double x = Math.random();
                double y = Math.random();
                return x * x + y * y < 1;
    }).count();

    final long duration = ((System.nanoTime() - start) / 1_000_000);

    System.out.println("  Parallel Version: PI ~ " + 4.0 * (count / (double) NUM_SAMPLES) + " calculated in "
        + duration + " msecs");
    }

}

结果:

Sequential Version: PI ~ 3.14176568 calculated in  4893 msecs
  Parallel Version: PI ~ 3.1417546  calculated in 12044 msecs
               Real PI:= 3.141592653589793

共有1个答案

师腾
2023-03-14

在我的机器上并行运行(3.0GHz英特尔酷睿i7,两个核心,四个线程)得到的结果更糟:

sequential: PI ~ 3.14175124 calculated in  4952 msecs
  parallel: PI ~ 3.14167776 calculated in 21320 msecs

我怀疑主要原因是math.random()是线程安全的,因此它会在每个调用周围同步。因为有多个线程同时尝试获取随机数,所以它们都在争夺同一个锁。这增加了大量的开销。请注意,math.random()的规范说明如下:

方法已正确同步,以允许多个线程正确使用。然而,如果许多线程需要以很高的速率生成伪随机数,那么每个线程拥有自己的伪随机数生成器可以减少争用。

long count = LongStream.rangeClosed(1, NUM_SAMPLES)
                       .parallel()
                       .filter(e -> {
                           ThreadLocalRandom cur = ThreadLocalRandom.current();
                           double x = cur.nextDouble();
                           double y = cur.nextDouble();
                           return x * x + y * y < 1;
                       })
                       .count();
sequential2: PI ~ 3.14169156 calculated in 1171 msecs
  parallel2: PI ~ 3.14166796 calculated in  648 msecs

这是1.8倍的加速比,对于一个双核的机器来说还不算太差。请注意,当按顺序运行时,这也会更快,可能是因为没有任何锁开销。

旁白:通常情况下,对于基准测试,我建议使用JMH。然而,这个基准似乎运行得足够长,它给出了相对速度的合理指示。为了得到更精确的结果,我推荐使用JMH。

更新

NUM_SAMPLES = 100_000_000

sequential:  PI ~ 3.14175124 calculated in    4952 msecs
parallel:    PI ~ 3.14167776 calculated in   21320 msecs
sequential2: PI ~ 3.14169156 calculated in    1171 msecs
parallel2:   PI ~ 3.14166796 calculated in     648 msecs

NUM_SAMPLES = 1_000_000_000

sequential:  PI ~ 3.141572896 calculated in  47730 msecs
parallel:    PI ~ 3.141543836 calculated in 228969 msecs
sequential2: PI ~ 3.1414865   calculated in  12843 msecs
parallel2:   PI ~ 3.141635704 calculated in   7953 msecs
 类似资料:
  • 问题内容: 我正在使用Java 8的流,无法理解我得到的性能结果。我有2个核心CPU(Intel i73520M),Windows 8 x64和64位Java 8 Update5。我正在对String的流/并行流进行简单映射,发现并行版本要慢一些。 考虑到我有2个CPU内核,并行版本是否应该更快?有人可以提示我为什么并行版本比较慢吗? 问题答案: 确实有几个并行发生的问题。 首先是并行解决问题总是

  • 考虑到我有2个CPU核心的事实,并行版本不是应该更快吗?有人能给我一个提示为什么并行版本比较慢吗?

  • 我正在尝试将ArrayList转换为Treemap,所以我编写了一个比较各种方法的基准: 1)并行流中的toMap 2)toMap在流中 3)流中forEach 主 大小为10_000的输出如下 “for loop”与预期一样是最慢的 回到正题,为什么当arraylist列表很大时,for each in parallel steam会失败? 运行i7 2670QM,因此线程池大小应为8

  • 问题内容: 它们看起来几乎一样,甚至是语法? 使用什么?或什么时候使用什么? 问题答案: 速度不再是真正活跃的开发。Freemarker是。 根据我的经验,Freemarker也更加灵活。

  • 我想知道Hashmap和ArrayList中的搜索元素有什么比较吗?我遇到的情况如下:我将有少量的元素(通常4-6个,最多10个)。我有这样一个元素的整数id,我将得到很多调用,这些调用将使用Id搜索元素并在这个元素上执行一些方法。这看起来是hashmap的一个很好的用例,但是我开始怀疑像ArrayList这样的线性集合在这里会不会更好。例如因为CPU缓存。为了在地图上使用搜索,我需要自动装箱来从

  • 我有一个关于compareTo函数如何帮助比较器排序的问题,即o1。比较(o2)与o2。比较(o1) 如果两个字符串相等,则此方法返回0,否则返回正值或负值。如果第一个字符串在词典上大于第二个字符串,则结果为正,否则结果为负。 上面的陈述很简单,但是为什么o1.compare(o2)会给我一个升序,而o2.compare(o1)给了我一个降序? 如果我有整数值“5,10,3”,我得到3,5,10和