Forkjoinpool VS 顺序性能
Forkjoinpool VS sequential perofrmance
我正在比较算法(前 n 个数字的总和)的顺序和并行性能(使用 ForkJoinPool):
public class ForkJoinSumCalculator extends RecursiveTask<Long> {
private static final ForkJoinPool FORKJOINPOOL = new ForkJoinPool();
private final long[] numbers;
private final int start;
private final int end;
public static final long THRESHOLD = 10_000;
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int numLoops = 40;
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(LongStream.rangeClosed(1, 100000000).toArray());
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
System.out.println("Total time parallel:"+ (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
for(int i = 1; i <= numLoops ; i++) {
long seqSum = 0L;
for(int j = 1; j <= 100000000 ; j++) {
seqSum = seqSum + j;
}
}
System.out.println("Total time sequential:"+ (System.currentTimeMillis() - startTime));
}
public ForkJoinSumCalculator(long[] numbers) {
this(numbers, 0, numbers.length);
}
private ForkJoinSumCalculator(long[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
....splitting the task
....or calculating the sum if size is less than THRESHOLD
}
}
我尝试改变 numLoops 的值范围很广,但顺序方法总是表现得更好,而且也是按 3-4 的顺序。
考虑到数组大小不是那么小,并行版本应该不会在这里表现得更好。
获得实际输出的一些建议:
1. 对于并行处理,您将在每次循环迭代中创建一个包含 10^8 个元素的长流。在 Java 中,创建一个新对象会做很多事情。因此,它正在影响并行处理的性能。在单个循环中,您根本没有创建任何对象。所以字段不一样不能比较
您可以创建一个实例,并且每次都使用该对象的引用。所以改变这部分:
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(LongStream.rangeClosed(1, 100000000).toArray());
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
对此:
long[] longs = LongStream.rangeClosed(1, 100000000).toArray();
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(longs);
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
2. 当您在顺序任务中进行求和时,您使用了原始变量来声明 seqSum,而您的并行任务正在使用盒装任务来 return.而且盒装计算比原始计算要花更多的时间。
此外,当您在并行任务中发送一个数组时,我猜(您没有在 post 中给出该代码),您正在使用该数组来获取总和。但是在顺序的中,您不需要访问任何引用的索引。相反,您是从迭代变量中获取值。对于10^8这样的数字,其实差很多
所以将那部分从:
for(int i = 1; i <= numLoops ; i++) {
long seqSum = 0L;
for(int j = 1; j <= 100000000 ; j++) {
seqSum = seqSum + j;
}
}
至:
for(int i = 1; i <= numLoops ; i++) {
Long seqSum = 0L;
for(int j = 1; j < 100000000 ; j++) {
seqSum = seqSum + longs[j];
}
}
在所有这些更改之后,在一台 4 处理器的机器上,我得到了这个:
Total time parallel:4461
Total time sequential:25542
并使用 8 个处理器的机器(更新了一台配置更好的机器):
Total time parallel:3157
Total time sequential:16863
最后但同样重要的是,你的问题给了我一些思考的要点。这就是为什么,谢谢!
编码愉快!
我正在比较算法(前 n 个数字的总和)的顺序和并行性能(使用 ForkJoinPool):
public class ForkJoinSumCalculator extends RecursiveTask<Long> {
private static final ForkJoinPool FORKJOINPOOL = new ForkJoinPool();
private final long[] numbers;
private final int start;
private final int end;
public static final long THRESHOLD = 10_000;
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int numLoops = 40;
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(LongStream.rangeClosed(1, 100000000).toArray());
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
System.out.println("Total time parallel:"+ (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
for(int i = 1; i <= numLoops ; i++) {
long seqSum = 0L;
for(int j = 1; j <= 100000000 ; j++) {
seqSum = seqSum + j;
}
}
System.out.println("Total time sequential:"+ (System.currentTimeMillis() - startTime));
}
public ForkJoinSumCalculator(long[] numbers) {
this(numbers, 0, numbers.length);
}
private ForkJoinSumCalculator(long[] numbers, int start, int end) {
this.numbers = numbers;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
....splitting the task
....or calculating the sum if size is less than THRESHOLD
}
}
我尝试改变 numLoops 的值范围很广,但顺序方法总是表现得更好,而且也是按 3-4 的顺序。
考虑到数组大小不是那么小,并行版本应该不会在这里表现得更好。
获得实际输出的一些建议:
1. 对于并行处理,您将在每次循环迭代中创建一个包含 10^8 个元素的长流。在 Java 中,创建一个新对象会做很多事情。因此,它正在影响并行处理的性能。在单个循环中,您根本没有创建任何对象。所以字段不一样不能比较
您可以创建一个实例,并且每次都使用该对象的引用。所以改变这部分:
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(LongStream.rangeClosed(1, 100000000).toArray());
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
对此:
long[] longs = LongStream.rangeClosed(1, 100000000).toArray();
for(int i = 1; i <= numLoops; i++) {
ForkJoinSumCalculator forkJoinSumCalculator = new ForkJoinSumCalculator(longs);
FORKJOINPOOL.invoke(forkJoinSumCalculator);
}
2. 当您在顺序任务中进行求和时,您使用了原始变量来声明 seqSum,而您的并行任务正在使用盒装任务来 return.而且盒装计算比原始计算要花更多的时间。
此外,当您在并行任务中发送一个数组时,我猜(您没有在 post 中给出该代码),您正在使用该数组来获取总和。但是在顺序的中,您不需要访问任何引用的索引。相反,您是从迭代变量中获取值。对于10^8这样的数字,其实差很多
所以将那部分从:
for(int i = 1; i <= numLoops ; i++) {
long seqSum = 0L;
for(int j = 1; j <= 100000000 ; j++) {
seqSum = seqSum + j;
}
}
至:
for(int i = 1; i <= numLoops ; i++) {
Long seqSum = 0L;
for(int j = 1; j < 100000000 ; j++) {
seqSum = seqSum + longs[j];
}
}
在所有这些更改之后,在一台 4 处理器的机器上,我得到了这个:
Total time parallel:4461
Total time sequential:25542
并使用 8 个处理器的机器(更新了一台配置更好的机器):
Total time parallel:3157
Total time sequential:16863
最后但同样重要的是,你的问题给了我一些思考的要点。这就是为什么,谢谢!
编码愉快!