Clojure计划的执行时间

时间:2012-03-25 06:30:25

标签: java performance clojure

这是一个clojure程序,用于从文件中读取整数并计算反转次数,即在序列中较小的数字之前出现较大数字的次数。它实现了一个O(n ^ 2)算法,并且在大小为100,000的输入上运行大约需要30分钟。

(defn count-inversions
  [file]
  (let [int-list (vec (map #(Integer/parseInt %)
                           (clojure.string/split (slurp file) #"\r\n")))]
    (loop [c 0
           t-list int-list]
      (let [size (count t-list)]
        (if (empty? t-list)
         c
         (recur (reduce #(if (< %2 (first t-list)) (inc %1) %1) c (subvec t-list 1 (dec  size)))
                (subvec t-list 1 (dec size))))))))

使用Java实现此算法只需几秒钟即可完成。为什么这么大的差异?

1 个答案:

答案 0 :(得分:5)

看起来对我来说可能很慢的事情(虽然你必须确定基准以确定......)

  • 您的所有号码都装箱了。这将使你做的任何事情比使用原语慢得多。它不是非常惯用,但如果你想获得这种加速,你可以在Clojure中使用原始数组。
  • 减少子目录当前会创建大量临时对象。有一个补丁正在解决这个问题(http://dev.clojure.org/jira/browse/CLJ-894),但你可能不得不等待Clojure 1.4或1.5。
  • 帮助(set! *unchecked-math* true)提高整数运算的性能(显然,如果可能发生溢出,你需要更加小心)。

如果我想让它在Clojure中运行得非常快,我可能会使用原始数组并使用原始索引进行循环:

(set! *unchecked-math* true)

(defn count-inversions [coll]
     (let [^ints integers (int-array coll)
           size (long (count integers))]
       (loop [c (long 0)
              i (long 0)]
         (if (>= i size)
           c
           (recur
             (loop [c (long c)
                    v (aget integers i)
                    j (inc i)]
               (if (>= j size)
                 c
                 (recur (long (if (> v (aget integers j)) (inc c) c)) v (inc j))))
             (inc i))))))

(time (count-inversions (for [i (range 100000)] (rand-int 100))))
"Elapsed time: 16036.651863 msecs"

即。这段代码在我的机器上大约16秒内计算100,000个整数的所有反转(使用Clojure 1.3)