首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ParallelArray或使用ForkJoinPool

ParallelArray或使用ForkJoinPool
EN

Stack Overflow用户
提问于 2021-03-08 10:00:24
回答 2查看 164关注 0票数 1

我必须提高以下代码的性能,但我不知道要改进什么(或如何改进)。

目前,代码需要大约。创建94.478.400对象的12秒。

我想,我可以通过使用类似ForkJoinPoolParallelArray之类的东西来加快速度,但是我不知道如何或在哪里将这种并行化放入我的代码中。

运行main-方法:

代码语言:javascript
复制
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ParallelArray {

    // class to hold the permutations
    public static class Perm {

        public Perm(double a, double b, double c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }

        double a;
        double b;
        double c;

        public String toString() {
            return "a=" + a + " b" + b + " c" + c;
        }
    }

    public static void main(String[] args) {

        var start = System.currentTimeMillis();
        var perms = new ArrayList<Perm>();

        // create first permutations
        var perm1 = computePerm(2).collect(Collectors.toList());

        // create second permutations
        var perm2 = computePerm(-2).collect(Collectors.toList());

        // now I permutate again with perm1 and perm2:
        // iterating is fast
        for (var p1 : perm1) {
            for (var p2 : perm2) {
                // but this operation needs too long
                // can I speed this up by using fork-join or parallelArray?
                perms.add(new Perm(p1.a + p2.a, p1.b + p2.b, p1.c + p2.c));
            }
        }

        var end = System.currentTimeMillis();

        // all the operations needs approx. 12 seconds
        System.out.println("diff: " + (end - start) + " millis");
        // it returns 94.478.400 objects
        System.out.println("objects: " + perms.size());
    }

    public static Stream<Perm> computePerm(int inc) {
        // I use a stream builder (to increase perfomance?)
        Stream.Builder<Perm> all = Stream.builder();
        // iterating is fast
        for (var a : arrayA().toArray()) {
            for (var b : arrayB().toArray()) {
                for (var c : arrayC().toArray()) {
                    // but this operation needs too long
                    // can I speed this up by using fork-join or parallelArray?
                    all.add(new Perm(a + inc, b + inc, c + inc));
                }
            }
        }

        return all.build();
    }

    public static IntStream arrayA() {
        return IntStream.rangeClosed(1, 30);
    }

    public static DoubleStream arrayB() {
        return DoubleStream.iterate(0, d -> d + 0.1).limit(18);
    }

    public static DoubleStream arrayC() {
        return DoubleStream.iterate(1, d -> d - 0.1).limit(18);
    }

}

编辑:

在玩完之后,我为computePerm(int )使用了另一种方法,它使用了parallelStream:

代码语言:javascript
复制
    public static Stream<Perm> computePermParallel(int inc) {
        Stream<Perm> all = arrayA().parallel().boxed().flatMap(a ->
        {
            return arrayB().parallel().boxed().flatMap(b ->
            {
                return arrayC().parallel().mapToObj(c ->
                {
                    return new Perm(a + inc, b + inc, c + inc);
                });
            });
        });
        return all;
    }

然而,当使用computePermParallel(int inc)而不是computePerm(int inc)时,性能甚至更糟糕:而不是大约。12秒,我得等一下。19秒因此,即使有大量的对象,非并行版本也比并行版本快得多。不知道为什么。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-03-08 15:32:54

对代码的一些建议

  • 不断扩大您的ArrayList会导致大量的拷贝和腰部时间。正如您已经知道的大小,在初始化时将其传递给您的ArrayList。您可以使用var perms = new ArrayList<Perm>(94478400);来测试这一点。同样的方法可以通过在arraylist perms.ensureCapacity(94478400);上调用以下方法来实现
  • 在您的例子中,使用流没有真正的优势,因为您创建了一个包含所有元素的流。然后将该流更改为列表。当您在收集之前应用某种过滤时,流就会有好处。在您的情况下,您只是重新分配从一个流到一个列表。可以直接将其添加到列表中(具有正确的初始值参数)。
  • ForkJoinPool:在您的场景中以并行方式运行并不会有任何帮助。如果你不得不做一些昂贵的计算,那会有帮助的。在您的示例中,只需创建一个新的Perm对象并将其放入perms列表中。最大的问题是ArrayList并不是线程安全的。像现在这样使用它会导致数据丢失。解决方案是让ForkJob返回完成后合并到完整列表中的临时列表。或者使用像CopyOnWriteArrayList这样的线程安全列表。这两种解决方案都会用数据创建更多的列表,并导致数据被复制。因此,与其改进运行时,它很可能会使其更加糟糕。
票数 1
EN

Stack Overflow用户

发布于 2021-03-08 10:49:21

有几件事对你不利。

  1. 9400万个简单对象就像用于该数据的2-4GB内存--您有足够的RAM /配置吗?
  2. 不断地重新分配/复制ArrayList备份--您可以帮助使用ArrayList的ensureCapacity方法(如果没有这种方法,当阵列耗尽容量时,备份存储必须增长,并且复制数据-这需要付出一定的代价,但没有任何收益)。
  3. 在Java中内存分配很慢。如果您可以将数据表示为3组双(而不是双倍)的数组--您可以大大减少内存分配的数量(3个对象分配vs 94+百万)--您只需要执行一些代码,然后初始化数据。时间应该要快得多(尽管你可能会遇到比你预期的糟糕的性能--如果它导致CPU缓存被破坏的话)。实际上,试着预先创建19400万行数组,看看需要多长时间.
  4. 不必要的临时调用-- arrayB()和arrayC()在arrayA()循环中的每个调用都会导致生成一个新的临时结果,然后将其丢弃。但在这个例子中,您可以这样做-然后只有3个调用(和临时调用),没有"nof B“* "nof C”* "nof A“+ "nof B”* "nof A“+.如果每个循环的结果都不同,那么忽略它,但是正如所写的,它读起来很好,但是运行很糟糕。

所以改变这个-

代码语言:javascript
复制
for (var a : arrayA().toArray()) {
for (var b : arrayB().toArray()) {
for (var c : arrayC().toArray()) { 

为了这个-

代码语言:javascript
复制
final int[] aArray = arrayA().toArray();
final double[] bArray = arrayB().toArray();
final double[] cArray = arrayC().toArray();
for (var a : aArray) {
for (var b : bArray) {
for (var c : cArray) { 

也许你会把它降低一个数量级--但我怀疑至少还有1到2秒--你使用了大量的内存--你真的需要这么做吗(这听起来像是这个案子的缺陷)?

为了让您了解内存与CPU的相对成本,您可以测试创建所有对象,然后循环并设置所有值和时间差。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66527737

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档