java - 使用 Java 8 Arrays.parallelSetAll() 的正确方法是什么?

标签 java concurrency parallel-processing java-8

这是我的示例程序:

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
*/

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

最佳答案

引用 Random Java文档:

Instances of java.util.Random are threadsafe. However, the concurrent use of the same java.util.Random instance across threads may encounter contention and consequent poor performance. Consider instead using java.util.concurrent.ThreadLocalRandom in multithreaded designs.

例如,您可以改用建议的 ThreadLocalRandom,这很可能会提供比顺序 setAll() 更好的性能:

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

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

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

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

关于java - 使用 Java 8 Arrays.parallelSetAll() 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34911358/

相关文章:

多线程模式下的Java集合算法

java - 将 JSON 文件读入集合,最佳实践

r - 为Ax = b并行求解Solve()吗?

java - 评估字符串内的变量

java - .peek() 在 java 8 流中的使用

scala - 使用 Scala 读取 Cassandra 中的并行性

java - 用于文件下载操作的多线程代码,与单线程相比并不快

c++ - 如何从 MPI 程序访问 MySQL(使用 MPICH2)?

java - 从 Jboss-as 7.1.1 中的 standalone.xml 外部化资源适配器配置

Java:检查泛型是否为 int