【问题标题】:What is the right way to use Java 8 Arrays.parallelSetAll()?使用 Java 8 Arrays.parallelSetAll() 的正确方法是什么?
【发布时间】:2016-01-20 21:54:33
【问题描述】:

这是我的示例程序:

import java.util.*;
import java.time.*;

class Randy {
  private Random r;
  // New generator for each instance:
  public Randy() { r = new Random(47); }
  public Integer get(int n) { return  r.nextInt(); }
}

public class ParallelSetAll {
  static int[] ia = new int[10_000_000];
  public static void main(String[] args) {
    Instant start1 = Instant.now();
    Arrays.setAll(ia, new Randy()::get);
    long nanos1 = Duration.between(start1, Instant.now()).toNanos();
    System.out.println(nanos1);

    Instant start2 = Instant.now();
    Arrays.parallelSetAll(ia, new Randy()::get);
    long nanos2 = Duration.between(start2, Instant.now()).toNanos();
    System.out.println(nanos2);
  }
}
/* Output:
223000000
1261000000
*/

注意parallelSetAll() 的运行速度比setAll() 慢多少。我的猜测是单个随机生成器会导致并行版本的各种流量开销,但我不确定,所以首先我想了解为什么会发生这种情况。

如何为parallelSetAll() 创建一个不会使其变慢的生成器?我怀疑这将是一个通过传入索引独立操作元素的函数。如n -> ia[n] * 10

正如有人指出的那样,我应该指出,这不是一个适当的微基准;你的旅费可能会改变。它旨在作为一种简单的方式来感受算法的工作方式,而不是用于微调。

下面是工作示例:

import java.util.*;
import java.util.concurrent.*;
import java.time.*;

public class ParallelSetAll {
  static long timeIt(Runnable test) {
    Instant start = Instant.now();
    test.run();
    long millis =
      Duration.between(start, Instant.now()).toMillis();
    System.out.println(millis);
    return millis;
  }
  static int get(int n) {
    return ThreadLocalRandom.current().nextInt();
  }
  public static void main(String[] args) {
    int[] ia = new int[40_000_000];
    timeIt(() ->
      Arrays.setAll(ia, ParallelSetAll::get));
    timeIt(() ->
      Arrays.parallelSetAll(ia, ParallelSetAll::get));
  }
}
/* Output:
482
198
*/

我已经简化了一些事情,并将时间单位更改为毫秒。

【问题讨论】:

    标签: java concurrency parallel-processing java-8


    【解决方案1】:

    引用RandomJavaDoc:

    java.util.Random 的实例是线程安全的。然而,并发 跨线程使用相同的java.util.Random 实例可能会遇到 争用和随之而来的糟糕表现。考虑改为使用 java.util.concurrent.ThreadLocalRandom 在多线程设计中。

    因此,您可以使用建议的 ThreadLocalRandom 代替,这很可能比连续的 setAll() 提供更好的性能:

    class ThreadLocalRandy {
      public ThreadLocalRandy() {}
      public Integer get(int n) { return ThreadLocalRandom.current().nextInt(); }
    }
    

    (但请记住,您的代码示例不是a proper micro-benchmark

    并行运行的基本思想是尽量避免争用,并使并行计算尽可能独立。

    parallelSetAll() 的典型用法将因此仅根据传入的数字计算一个值,或者从不需要同步读取的源(例如另一个数组或集合)检索一些值。

    【讨论】:

    • 当然,如果你想要一个int数组,用随机值并行填充,你只需要调用new SplittableRandom().ints(10_000_000).parallel().toArray()...
    • 当然。除非这是您第一次听说SplittableRandom。真是一堂不错的课,谢谢。我想知道还有哪些其他聪明的课程还没有出现在我的雷达上。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 2021-09-23
    • 2017-04-07
    相关资源
    最近更新 更多