【问题标题】:Java code execution time issueJava代码执行时间问题
【发布时间】:2017-09-11 16:16:21
【问题描述】:

在我对网络扩散的研究中,我有以下代码为顶点建模了一个轻量级框架。最初的原型来自python中的一个框架,我将其翻译成Java。我遇到的问题是,虽然这段代码运行得比它的 python 版本快得多,最多 10000 个顶点,但对于更多的顶点(100,000+),它会停止。事实上,python 版本在 1.2 分钟内执行,而 java 版本即使在执行 7 分钟后也没有返回。我不确定为什么相同的代码会在更多的顶点处发生故障,我需要帮助来修复代码。

import java.util.*;

public class Vertex
{
  private int id;
  private HashMap<Integer, Double> connectedTo;
  private int status;

  public Vertex(int key)
  {
    this.id = key;
    this.connectedTo = new HashMap<Integer, Double>();
    this.status = 0;
  }

  public void addNeighbour(int nbr, double weight)
  {
    this.connectedTo.put(nbr, weight);
  }

  public int getId()
  {
    return this.id;
  }

  public double getWeight(int nbr)
  {
    return this.connectedTo.get(nbr);
  }

  public int getStatus()
  {
    return this.status;
  }

  public Set<Integer> getConnections()
  {
    return this.connectedTo.keySet();
  }

//testing the class

  public static void main(String[] args)
  {
    int noOfVertices = 100000;

    Vertex[] vertexList = new Vertex[noOfVertices];

    for (int i = 0; i < noOfVertices; i++) {
        vertexList[i] = new Vertex(i);
    }

    for (Vertex v : vertexList) {
        int degree = (int)(500*Math.random()); //random choice of degree 
        int neighbourCount = 0; // count number of neighbours built up

        while (neighbourCount <= degree) {
            int nbr = (int) (noOfVertices * Math.random()); // randomly choose a neighbour
            double weight = Math.random(); // randomly assign a weight for the relationship
            v.addNeighbour(nbr, weight);
            neighbourCount++;
        }
    }

  }
}

作为参考,这段代码的python版本如下:

import random

class Vertex:
    def __init__(self, key):
      self.id = key
      self.connectedTo = {}

    def addNeighbor(self, nbr, weight=0):
      self.connectedTo[nbr] = weight

    def __str__(self):
      return str(self.id) + ' connectedTo: ' \
          + str([x.id for x in self.connectedTo])

    def getConnections(self):
      return self.connectedTo.keys()

    def getId(self):
      return self.id

    def getWeight(self, nbr):
      return self.connectedTo[nbr]

if __name__ == '__main__':
  numberOfVertices = 100000
  vertexList = [Vertex(i) for i in range(numberOfVertices)] # list of vertices

  for vertex in vertexList:
    degree = 500*random.random() 
    # build up neighbors one by one
    neighbourCount = 0 

    while neighbourCount <= degree:
        neighbour = random.choice(range(numberOfVertices))
        weight = random.random() # random choice of weight
        vertex.addNeighbor(neighbour, weight)
        neighbourCount = neighbourCount + 1

【问题讨论】:

  • 我目前正在研究这个,很快就会发布一些优化的代码!
  • 如果不进行分析就很难分辨,实际上几乎可以在任何地方。只是一个快速点:看一下 java.util.Random 类,它有一个 nextInt(bound) 方法(它不太可能是一个相当大的加速,但仍然)。
  • 找到了解决办法,贴在下面!

标签: java python performance collections


【解决方案1】:

这是一个非常有趣的问题,我相信我也学到了一些新东西。我尝试以不同的方式优化代码,例如使用并行流以及使用ThreadLocalRandom,它可以比Random 快三倍。然而,我终于发现了主要的瓶颈:分配给 JVM 的内存。

因为您有太多元素要添加到您的Map(最坏的情况是 500,000 和 100,000 个顶点),您将需要大量内存(堆空间)。如果允许 JVM 动态分配内存,那么程序将需要很长时间才能执行。我解决这个问题的方法是通过将-Xms3G 作为VM 参数应用到程序的运行配置,为JVM 预先分配内存(特别是3 GB),这可以在您的IDE 或终端中完成。

我还对您的代码进行了一些优化,我将在下面发布(对我来说只需几秒钟即可完成):

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

public class Test {

    private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();

    public static void main(String[] args) {
        int noOfVertices = 100_000;

        Vertex[] vertexList = new Vertex[noOfVertices];

        IntStream.range(0, noOfVertices).parallel().forEachOrdered(i -> {
            vertexList[i] = new Vertex(i);

            int degree = (int) (500 * RANDOM.nextDouble()); // random choice of degree

            for (int j = 0; j <= degree; j++) {
                int nbr = (int) (noOfVertices * RANDOM.nextDouble()); // randomly choose a neighbor

                vertexList[i].addNeighbour(nbr, RANDOM.nextDouble());
            }
        });
    }

}

class Vertex {

    private int id;

    private Map<Integer, Double> connectedTo;

    private int status;

    public Vertex(int id) {
        this.id = id;

        this.connectedTo = new HashMap<>(500);
    }

    public void addNeighbour(int nbr, double weight) {
        this.connectedTo.put(nbr, weight);
    }

    public int getId() {
        return this.id;
    }

    public double getWeight(int nbr) {
        return this.connectedTo.get(nbr);
    }

    public int getStatus() {
        return this.status;
    }

    public Set<Integer> getConnections() {
        return this.connectedTo.keySet();
    }

}

我不确定在多线程环境中使用 ThreadLocalRandom 的明确后果,但如果您愿意,可以将其切换回 Math#random

【讨论】:

  • 一个相当优雅的解决方案,我没有考虑过。感谢您的努力。
  • @buzaku 不客气,我真的不知道分配堆空间会影响性能,但我很高兴你的问题得到了解决!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 1970-01-01
  • 2022-07-04
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多