我正在比较算法(前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
}
}
我试着用不同的NumLoop来获得不同的值,但顺序法总是表现得更好,而且也是按3-4的顺序。
考虑到阵列大小并不是那么小,并行版本在这里的性能不应该更好吗。
获得实际输出的一些建议:
1.我不知道你的机器上有多少个处理器。但是你在代码中使用了默认的。所以在这种情况下,基本上你没有得到并行处理的性能。当你创建ForkJoinpool的对象时,你需要发送你想要计算的处理器号。所以从
private static final ForkJoinPool FORKJOINPOOL = new ForkJoinPool();
为此:
private static final ForkJoinPool FORKJOINPOOL = new ForkJoinPool(8);
注意:对于我的机器,可用处理器的数量是8。所以我发这个。您可以从以下输出中获得您的:
System.out.println(Runtime.getRuntime().availableProcessors());
2.对于并行处理,在每个循环迭代中创建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);
}
3.当您在顺序1中进行求和时,您使用原语变量来声明seqSum,而您的并行任务使用装箱的变量来返回。盒装计算比原始计算需要更多的时间。
另外,当你在并行任务中发送一个数组时,我猜(你在帖子中没有给出代码),你是在使用这个数组来获得和。但在顺序索引中,不需要访问任何引用的索引。而是从迭代变量中获取值。对于像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
最后但并非最不重要的是,你的问题给了我一些思考的要点。这就是为什么,谢谢!
快乐编码!
问题内容: 我试图理解这段代码,不确定为什么第二遍在第一遍之前执行。如果有人真的可以帮助我,那就太好了! 输出: 问题答案: 您没有任何内容可以显式同步两个goroutine的顺序。如果运行足够的时间,您将看到调用以不同的顺序进行打印。当执行goroutine时,由于它们是并发操作,因此无法保证它们将何时执行和/或完成。您需要使用各种标准库程序包或通道本身来同步并发运行的goroutine的执行。
类D 主要方法是 Bean配置文件是 程序的输出为:
我无法确定spring security在何时何地执行身份验证管理器。我的意思是,certian过滤器按如下顺序执行: 但是当身份验证提供者对提供的用户名和密码进行身份验证时,我的意思是问下面这些过滤器是身份验证提供者执行的。 问候贾延德拉
问题内容: 我正在学习Go,并且遇到了以下代码片段: 有人可以告诉我为什么“ sum”函数的第二个调用在第一个调用之前通过通道吗?在我看来,输出应为: 我还使用无缓冲通道对此进行了测试,它也给出了相同顺序的输出。我想念什么? 问题答案: 您正在代码中调用go例程,但无法确定例程何时结束并将该值传递到缓冲通道。 由于该代码是异步的,因此只要例程完成,它将把数据写入通道,并在另一侧读取。在上面的示例中
问题内容: 输出: 我大致了解装饰器,以及在大多数示例中它如何与装饰器一起使用。 在此示例中,有2个。从输出看,似乎先执行,然后执行。 这是否意味着对于装饰功能,它将首先运行该功能,然后移至其他装饰器的顶部?像先那么,而不是相反。 所以这意味着它与大多数编程语言中的自顶向下方法的规范不同吗?仅用于这种装饰器吗?还是我错了? 问题答案: 装饰器 包装 正在装饰的功能。这样就修饰了装饰器的结果,从而修
主要内容:1 概述,2 测试执行顺序,3 例子,4 结论1 概述 在本指南中,我们将学习如何按顺序执行测试。默认情况下,JUnit以任何顺序执行测试。 2 测试执行顺序 要更改测试执行顺序,只需使用@FixMethodOrder注释测试类并指定可用的MethodSorters之一: @FixMethodOrder(MethodSorters.JVM):按照JVM返回的顺序保留测试方法。此顺序可能因运行而异。 @FixMethodOrder(Method