【问题标题】:Replace nested for loops with parallel stream - Java用并行流替换嵌套的 for 循环 - Java
【发布时间】:2018-01-31 09:13:57
【问题描述】:

我正在努力提高性能至关重要的程序的速度。目前它无法处理大型数据集。有很多嵌套的 for 循环,所以我认为值得尝试并行流。我可以访问高性能集群,因此可能有许多可用内核。 我有以下方法:

public MinSpecSetFamily getMinDomSpecSets() {
        MinSpecSetFamily result = new MinSpecSetFamily();
        ResourceType minRT = this.getFirstEssentialResourceType();
        if (minRT == null || minRT.noSpecies()) {
            System.out.println("Problem in getMinDomSpecSets()");
        }
        for (Species spec : minRT.specList) {
            SpecTree minTree = this.getMinimalConstSpecTreeRootedAt(spec);
            ArrayList<SpecTreeNode> leafList = minTree.getLeaves();
            for (SpecTreeNode leaf : leafList) {
                ArrayList<Species> sp = leaf.getAncestors();
                SpecSet tmpSet = new SpecSet(sp);
                result.addSpecSet(tmpSet);
            }
        }
        return result;
    }

我知道我可以将嵌套的 for 循环转换为并行流,例如:

minRT.specList.parallelStream().flatMap(leaf -> leaflist.parallelStream())

但是,我找不到显示如何处理每个 for 循环中的操作的示例,而且我对它应该如何工作完全没有信心。我非常感谢有关如何转换此方法的一些帮助和解释,以便我也可以将解决方案转换为程序中的其他方法。 谢谢。

【问题讨论】:

  • 您已经分析了软件,确定了热点,现在您对应该尝试优化的内容有了一个很好的想法?您将“将嵌套循环转换为并行流”基于实际数据,而不仅仅是猜测?
  • 是的,我尝试过使用分析并确定此方法是占用最多 (99.7%) 处理时间的方法。我不认为这是由于方法不佳,而是纯粹的计算量。

标签: java parallel-processing java-8 java-stream


【解决方案1】:

这是一种方法(希望我没有错别字):

MinSpecSetFamily result =
    minRT.specList
         .parallelStream()
         .flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
         .map(leaf -> new SpecSet(leaf.getAncestors()))
         .reduce(new MinSpecSetFamily (),
                 (fam,set)-> {
                     fam.addSpecSet(set);
                     return fam;
                 },
                 (f1, f2) -> new MinSpecSetFamily(f1, f2));

编辑:按照 Holger 的评论,您应该使用 collect 而不是 reduce

MinSpecSetFamily result =
    minRT.specList
         .parallelStream()
         .flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
         .map(leaf -> new SpecSet(leaf.getAncestors()))
         .collect(MinSpecSetFamily::new,MinSpecSetFamily::addSpecSet,MinSpecSetFamily::add);

【讨论】:

  • 感谢您的帮助。因此,如果我创建一个新的构造函数,例如:public MinSpecSetFamily(MinSpecSetFamily one, MinSpecSetFamily two),它返回一个结合两者的新对象,我将如何替换组合器?
  • @SteveW (f1,f2)-&gt;new(f1,f2)。也许MinSpecSetFamily::new 也可以。但是,您不必返回新实例。将一个实例的数据添加到另一个实例并返回另一个实例可能更有效。
  • 刚刚做了一些测试。在小型数据集上,parallelstream 代码稍微慢一些。在问题数据集上,getLeaves 方法被调用了数百万次。使用嵌套的 for 循环需要 2 分 3 秒才能达到 5000 万次 getLeaves 调用。在我的 4 核机器上,使用并行流需要 1 分 4 秒。我不确定这是解决方案,但肯定会有所帮助。再次感谢
  • 您是否知道您可能在同一个实例上同时调用fam.addSpecSet(set),然后在f1f2 中将该单个实例传递给new MinSpecSetFamily(f1, f2)?这不是 reduce 的用途。你肯定想改用collect
  • 方法命名也可能没有帮助,因为 reduce 执行 Reduction 并且 collect 执行 Mutable Reduction...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 2018-05-21
  • 2019-06-06
  • 2016-07-14
  • 2019-04-05
  • 2022-01-14
  • 2015-08-03
相关资源
最近更新 更多