Clojure中向量和数组的性能

时间:2015-04-06 15:39:09

标签: performance clojure

我正在尝试解决Maximum subarray problem on hacker rank。这是一个标准的DP问题,我写了一个O(n)解决方案:

(defn dp
  [v]
  (let [n (count v)]
    (loop [i 1 f (v 0) best f]
      (if (< i n) 
        (let [fi (max (v i) (+ f (v i)))]
          (recur (inc i) fi (max fi best)))
        best))))


(defn positive-only
  [v]
  (reduce + (filterv #(> % 0) v)))


(defn line->ints
  [line]
  (->> 
   (clojure.string/split line #" ")
   (map #(Integer. %))
   (into [])
   ))


(let [T (Integer. (read-line))]
  (loop [test 0]
    (when (< test T)
      (let [_ (read-line)
            x (read-line)
            v (line->ints x)
            a (dp-array v)
            b (let [p (positive-only v)]
                (if (= p 0) (reduce max v) p))]
        (printf "%d %d\n" a b))
      (recur (inc test)))))

令我惊讶的是,我对一个大型测试案例的时间有限。我下载了输入文件,发现上面的版本需要大约3秒才能运行。

我认为瓶颈在(v i)(获得向量v中的第i个元素)。所以我将数据结构从向量更改为数组:

(defn dp-array
  [v0]
  (let [v (into-array v0)
        n (int (alength v))]
    (loop [i 1
           f (aget v 0)
           best f]
      (if (< i n) 
        (let [fi (max (aget v i) (+ f (aget v i)))]
          (recur (inc i) fi (max fi best)))
        best))))

这个阵列版本甚至更慢。在相同的输入上,它花费33秒,比矢量版本慢得多。我认为缓慢是由于拳击和拆箱。我尝试添加类型提示,但遇到了运行时错误。任何人都可以帮我改进dp-array功能吗?谢谢!

另外,非常感谢有人知道如何改进矢量版本。

更新

最后,我设法接受了我的clojure程序,而不是通过优化动态编程功能,而是将(Integer. str)更改为(Integer/parseInt str)。这样,在从字符串转换为整数时避免了反射。

我还将into-array替换为int-array。但两个版本的速度仍然相互提升。我希望数组版本比矢量版本快。

1 个答案:

答案 0 :(得分:2)

Clojure编译器无法推断v函数的数组版本中dp-array的类型,其参数v0具有未知类型。在评估以下alengthaget时,这会导致反映成本。为了避免这些不必要的反射,您必须将into-array替换为long-array