Java:ArrayList瓶颈

Java:ArrayList瓶颈,java,optimization,arraylist,Java,Optimization,Arraylist,在分析一个计算数千个元素的分层集群的java应用程序时,我意识到ArrayList.get占用了执行集群化部分所需的一半CPU 该算法搜索两个更相似的元素(因此是O(n*(n+1)/2)),下面是伪代码: int currentMax = 0.0f for (int i = 0 to n) for (int j = i to n) get content i-th and j-th if their similarity > currentMax u

在分析一个计算数千个元素的分层集群的java应用程序时,我意识到
ArrayList.get
占用了执行集群化部分所需的一半CPU

该算法搜索两个更相似的元素(因此是O(n*(n+1)/2)),下面是伪代码:

int currentMax = 0.0f
for (int i = 0 to n)
  for (int j = i to n)
    get content i-th and j-th
      if their similarity > currentMax
        update currentMax

merge the two clusters
所以实际上有很多
ArrayList.get
参与其中

有没有更快的办法?我想既然
ArrayList
应该是一个引用的线性数组,那么这应该是最快的方法,也许我什么都做不了,因为有太多的
get
s。。但也许我错了。我认为使用
HashMap
是行不通的,因为我需要在每次迭代中都获取它们,而且
map.values()
应该由
ArrayList
支持

否则,我应该尝试其他更优化的集合库吗?就像谷歌的,或者apache的

编辑:

你多少证实了我的怀疑:(

尝试并行化时,我的性能会得到提升吗?可能会使用一个执行器池来计算多对执行器的相似性……但我不知道数据结构上的同步和锁定是否最终会降低性能

使用两个内容的标记映射的点积计算相似度。映射是两个
HashMap
。此外,我已经将相似度缓存在
TLongFloatHashMap
(来自Trove集合)为了避免在以后的迭代中重新计算它,
Long
键被计算为两个内容的hashcode(这对于这对内容是唯一的,因此
hash(c1,c2)==hash(c2,c1)
),所以其他所有内容都已经进行了充分的调优

EDIT2:

为了让您更好地理解,我将发布一点代码。这用于计算用于存储两个元素之间相似性的哈希:

private long computeKey(int h1, int h2) {   
        if (h1 < h2) {
            int swap = h1;
            h1 = h2;
            h2 = swap;
        }           
        return ((long)h1) << 32 | h2;
    }
private long computeKey(inth1,inth2){
if(h1

我本来只会使用一个矩阵来存储所有值,但在每次迭代中,最相似的项都会从列表中删除,并添加一个新项(根据所选的两个项有一个新的标记映射)

您使用的算法是O(n²)。除非你有办法让你的算法比两两比较做得更好,否则性能不太可能有明显的提高-(

冒着陈述显而易见的风险,使用以下伪代码可能会加快速度:

int currentMax = 0.0f
for (int i = 0 to n)
  get content i-th
  for (int j = i to n)
    get content j-th
      if their similarity > currentMax
        update currentMax

merge the two clusters
尽管如此,它仍然是
O(n²)
。如果你需要将每个元素与其他元素进行比较,以找出哪一对最接近,你无法击败
O(n²)

这就是说,如果您多次调用此函数,那么在可排序映射中缓存这些结果时可以找到优化

编辑:如果相似性非常简单(例如,一维值,如高度),则可以首先对数组中的项进行排序,这样元素[0]与元素[1]最相似,元素[1]与元素[0]或元素[2]最相似。在这种情况下,可以获得高达
O(n lg n)
的速度


EDIT2:考虑到你的相关代码,你的基准测试结果是非常可疑的。我无法想象这两种情况比调用相关代码(即使假设缓存在绝大多数时间都被命中)花费更多的时间,这也被称为
O(n²)
次。如果get()是瓶颈,那么spong在将它们转换为数组方面也做得很好。

上面的代码中没有太多复杂的操作。主要是简单的数字读取/检查/写入。它们速度惊人


问题是
.get()是
是一个函数调用-与简单的
+
=
ArrayList.get相比,它将慢得多。
=
ArrayList.get是一个if语句,后跟一个数组访问。没有太多的优化。ArrayList.get占用了一半的执行时间,因为您没有做任何其他事情。tim中的重要因素e take是迭代次数,而不是for循环中的迭代次数。

没有O(n*(n+1)/2)。您的算法是O(n2)。有关更详细的解释,请参阅

Ben是正确的:通过将第i个元素置于内部循环之外,可以减少
get()
调用

你真正想要的是在O(n2)的基础上改进的东西,这需要能够对元素做出额外的假设,这取决于你所说的“相似性”

两种常见的方法:

  • 对列表进行排序并合并。总的来说,这是O(n log n)
  • 将一个列表放入具有(近似)常量查找的
    Map
    中。这可以根据
    Map
    的类型和遍历的性质将算法减少到O(n)和O(n log n)之间的任意位置

但这一切都取决于你所说的“相似性”是什么意思.

如果你在重复这个过程,每次都找到下一个最相似的对,你最好创建一个从i,j对到相似性度量的映射-这取决于计算相似性的处理器密集程度,以及你有多少项,以及你有多少内存。

除了算法效率之外,你正在调用
get
次数太多。当前调用
get
(按)
2*size*size的顺序)<
for (int j = 0; j < clusters.size(); ++j) {
                skip = false;

                for (int k = j+1; k < clusters.size(); ++k) {                                   
                    float r = correlation(clusters.get(k).tags, clusters.get(j).tags, clusters.get(k), clusters.get(j));

                    if (r > max) {
                        max = r;
                        i1 = j;
                        i2 = k;
                    }

                    if (max == 1.0f) {
                        skip = true;
                        break;
                    }
                }

                if (skip)
                    break;
            }
int currentMax = 0.0f
for (int i = 0 to n)
  get content i-th
  for (int j = i to n)
    get content j-th
      if their similarity > currentMax
        update currentMax

merge the two clusters
for (int j = 0; j < clusters.size(); ++j) {
  skip = false;
  HierarchNode jnode = clusters.get(j);

  for (int k = j+1; k < clusters.size(); ++k) {
    HierarchNode knode = clusters.get(k);
    float r = correlation(knode.tags, jnode.tags, knode, jnode);

    ... etc ...
HierarchNode[] clusterArr = clusters.toArray(new HierarchNode[clusters.size()]);
    public class WHN implements Comparable<WHN>{
        private HierarchNode node;
        private float weight;

        public HierarchNode getNode() {return node;}
        public float getWeight() {return weight;}

        public WHN(HierarchNode node, float weight) {this.node = node;this.weight = weight;}

        public int compareTo(WHN o) {return Float.compare(this.weight, o.weight); }
    }

    Map<Tag,<SortedMap<Float,HierarchNode>> map = new HashMap<Tag,List<WHN>> 
    for (HierarchNode n : cluster){
    for (Map.Entry tw : n.tags.entrySet()){
        Tag tag = tw.getKey();
        Float weight = tw.getValue();
        if (!map.ContainsKey(tag)){
            map.put(tag,new ArrayList<WHN>();
        }
        map.get(tag).add(new WHN(n,weight));
    }
    for(List<WHN> l: map.values()){
        Collections.Sort(l);
    }
}