当前位置: 首页 > 知识库问答 >
问题:

Forkjoinpool VS顺序执行

左丘成仁
2023-03-14

我正在比较算法(前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个答案

易品
2023-03-14

获得实际输出的一些建议:

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