这是我的示例程序:
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
*/
我简化了一些事情,并将时间单位更改为毫秒。
答案 0 :(得分:4)
引用Random JavaDoc:
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()
的典型用法因此将仅根据传入的数量计算值,或从不需要读取同步的源中检索某些值,例如另一个数组或集合。