为什么Clojure中的Java原生HashMap运行缓慢?

6

我将一个键与哈希映射关联了10000000次。以下是Java代码和输出:

import java.util.HashMap;

public class TestMap {
    public static void main(String[] args) {
        HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>();
        long  start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            mp.put(1, 1);
        }
        long end = System.currentTimeMillis();
        System.out.println("Elapsed time: " + (end - start) + " msecs");
    }
}


$ javac TestMap.java && java -cp . TestMap
Elapsed time: 38 msecs

然后我在REPL中从Clojure调用Java:

user=> (import java.util.HashMap)
java.util.HashMap
user=> (def mp (HashMap.))
#'user/mp
user=>  (time (dotimes [n 10000000] (.put mp 1 1)))
"Elapsed time: 10024.797 msecs"
nil

这两段代码的功能相同,但Clojure版本运行速度非常慢!!

问题出在哪里?


1
顺便说一句,Clojure 只需三行代码(而且更易读),这太棒了。 - erturne
3个回答

13

添加类型提示更好:

user> (import 'java.util.HashMap)
java.util.HashMap
user> (def mp (HashMap.))
#'user/mp
user> (time (dotimes [n 10000000] (.put mp 1 1)))
"Elapsed time: 13932.248126 msecs"
nil
user> (time (dotimes [n 10000000] (.put ^HashMap mp 1 1)))
"Elapsed time: 117.915992 msecs"
nil

有关类型提示的更多信息,请参阅 http://clojure.org/java_interop#Java%20Interop-Type%20Hints。 - Jon Gauthier

9
首先,针对这类性能问题的第一步是打开反射警告并移除任何警告。
 (set! *warn-on-reflection* true)

同时,loop 和 recur 的开销最低。


0

您也可以通过在声明中声明 HashMap 大小来提高 Java 代码速度

HashMap<Integer, Integer> mp = new HashMap<Integer, Integer>(10000000);

我猜这也是REPL的一种方式(我不知道它),是否可以保留内存空间?


这是正确的,但在这种情况下并没有太大的区别 - 真正的问题在于反射。 - mikera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接