是什么让我的代码使用如此多的内存?

时间:2015-01-06 07:57:47

标签: java performance scala memory

我已经以各种方式解决了CodeEval上的一个简单问题,可以找到here的规范(只有几行)。

我已经制作了3个工作版本(其中一个在Scala中)并且我不理解我上一个Java版本的性能差异,我认为这是最好的时间和内存方式。

我还将其与Github上的代码进行了比较。以下是CodeEval返回的性能统计信息:

Stats

。版本1是在Github上找到的版本 。版本2是我的Scala解决方案:

object Main extends App {
    val p = Pattern.compile("\\d+")
    scala.io.Source.fromFile(args(0)).getLines
        .filter(!_.isEmpty)
        .map(line => {
            val dists = new TreeSet[Int]
            val m     = p.matcher(line)
            while (m.find) dists += m.group.toInt

            val list  = dists.toList
            list.zip(0 +: list).map { case (x,y) => x - y }.mkString(",")
        })
        .foreach(println)
}

。版本3是我的Java解决方案,我期望它是最好的:

public class Main {
    public static void main(String[] args) throws IOException {
        Pattern        p    = Pattern.compile("\\d+");
        File           file = new File(args[0]);
        BufferedReader br   = new BufferedReader(new FileReader(file));

        String line;
        while ((line = br.readLine()) != null) {
            Set<Integer> dists = new TreeSet<Integer>();
            Matcher      m     = p.matcher(line); 
            while (m.find()) dists.add(Integer.parseInt(m.group()));

            Iterator<Integer> it   = dists.iterator();
            int               prev = 0;
            StringBuilder     sb   = new StringBuilder();
            while (it.hasNext()) {
                int curr = it.next();
                sb.append(curr - prev);
                sb.append(it.hasNext() ? "," : "");
                prev = curr;
            }
            System.out.println(sb);
        }
        br.close();
    }
}
  • 版本4与版本3相同,只是我没有使用StringBuilder来打印输出,并且在版本1中也是如此

以下是我对这些结果的解释:

  • 由于System.out.print次呼叫数量过多,版本1速度太慢。此外,在非常大的行上使用split(在执行的测试中就是这种情况)会占用大量内存。

  • 版本2似乎也很慢,但主要是因为&#34;开销&#34;在CodeEval上运行Scala代码,甚至非常高效的代码运行缓慢

  • 版本2使用不必要的内存来构建集合中的列表,这也需要一些时间,但不应该太重要。编写更高效的Scala可能会喜欢用Java编写它,所以我更喜欢优雅的性能

  • 在我看来,版本3不应该使用那么多内存。使用StringBuilder对内存的影响与在版本2中调用mkString具有相同的影响

  • 版本4证明对System.out.println的调用正在减慢程序的速度

有人看到对这些结果的解释吗?

3 个答案:

答案 0 :(得分:3)

我进行了一些测试。

每种语言都有一个基线。我用java和javascript编写代码。对于javascript,这是我的测试结果:

random challenge

  • 第1版:JS的默认空样板,带有标准输出消息
  • Rev 2:同样没有文件阅读
  • 第3版:只是标准输出的消息

您可以看到,无论如何,运行时间至少为200毫秒,内存使用量约为5兆。此基线也取决于服务器的负载!曾经有一段时间代码被严重超载,因此无法在最长时间(10秒)内运行任何东西。

检查一下,这是一个与前一个完全不同的挑战: Random pic 2.

  • Rev4:我的解决方案
  • Rev5:现在再次提交相同的代码。获得了8000多个排名点。 :d

结论:我不会太担心CPU和内存使用情况以及排名。这显然不可靠。

答案 1 :(得分:2)

你的scala解决方案很慢,不是因为“CodeEval上的开销”,而是因为你正在构建一个不可变的TreeSet,所以逐个添加元素。用

之类的东西替换它
val regex = """\d+""".r // in the beginning, instead of your Pattern.compile
...
.map { line => 
    val dists = regex.findAllIn(line).map(_.toInt).toIndexedSeq.sorted
...

应该减少约30-40%的执行时间。

相同的方法(构建列表,然后排序)可能会帮助你在“版本3”中使用内存(java集是真正的内存生成器)。在你的列表中给你的列表一个初始大小也是一个好主意(否则,每当它耗尽容量时它将增长50%,这在内存和性能上都是浪费的)。 600听起来像一个很好的数字,因为这是问题描述中城市数量的上限。

现在,既然我们知道上边界,那么更快更苗条的方法就是取消列表和盒装的Integeres,并且只做int dists[] = new int[600];

如果你想变得非常花哨,你还可以利用描述中提到的“路线长度”范围。例如,不是将int注入数组并进行排序(或保留树集),而是创建一个20,000位的数组(甚至是20K字节的速度),并设置您在读取时看到的那些... ...比任何解决方案都更快,内存效率更高。

答案 2 :(得分:1)

我尝试解决这个问题,并认为你不需要城市的名称,只需要排序数组中的距离。

它具有更好的738ms运行时间,以及4513792的内存。

虽然这可能无法帮助您改进代码,但这似乎是解决问题的更好方法。欢迎任何进一步改进代码的建议。

import java.io.*;
import java.util.*;

public class Main {
public static void main (String[] args) throws IOException {
    File file = new File(args[0]);
    BufferedReader buffer = new BufferedReader(new FileReader(file));
    String line;
    while ((line = buffer.readLine()) != null) {
        line = line.trim();

        String out = new Main().getDistances(line); 

        System.out.println(out);
    }
}

public String getDistances(String s){

    //split the string
    String[] arr = s.split(";");

    //create an array to hold the distances as integers
    int[] distances = new int[arr.length];

    for(int i=0; i<arr.length; i++){
         //find the index of , - get the characters after that - convert to integer - add to distances array
        distances[i] = Integer.parseInt(arr[i].substring(arr[i].lastIndexOf(",")+1));    
    }

    //sort the array
    Arrays.sort(distances);

    String output = "";
    output += distances[0]; //append the distance to the closest city to the string

    for(int i=0; i<arr.length-1; i++){

        //get distance between current element(city) and next
        int distance_between = distances[i+1] - distances[i];

        //append the distance to the string
        output += "," + distance_between;
    }


    return output;   
 }
 }