Clojure 1.3中的功能性能

时间:2012-03-12 03:53:23

标签: performance clojure

我想知道是否有人可以帮助我在Clojure 1.3中使用此代码段的性能。我正在尝试实现一个简单的函数,它需要两个向量并完成一系列产品。

因此,假设向量是X(大小为10,000个元素)和B(大小为3个元素),并且乘积之和存储在向量Y中,数学上看起来像这样:

Y0 = B0 * X2 + B1 * X1 + B2 * X0

Y1 = B0 * X3 + B1 * X2 + B2 * X1

Y2 = B0 * X4 + B1 * X3 + B2 * X2

依旧......

对于此示例,Y的大小最终为9997,对应于(10,000 - 3)。我已经设置了接受任何大小的X和B的函数。

以下是代码:它基本上从X一次获取(count b)个元素,将其反转,将*映射到B并对结果序列的内容求和,以生成Y元素。

(defn filt [b-vec x-vec]
  (loop [n 0 sig x-vec result []]
    (if (= n (- (count x-vec) (count b-vec)))
      result
      (recur (inc n) (rest sig) (conj result (->> sig
                                                  (take (count b-vec))
                                                  (reverse)
                                                  (map * b-vec)
                                                  (apply +)))))))

让X为(vec (range 1 10001))且B为[1 2 3]时,此功能大约需要6秒才能运行。我希望有人可以建议改进运行时间,无论是算法,还是我可能滥用的语言细节。

谢谢!

P.S。我已完成(set! *warn-on-reflection* true)但未收到任何反映警告信息。

3 个答案:

答案 0 :(得分:7)

你不必多次使用计数。下面的代码计算只计算一次

(defn filt [b-vec x-vec]
  (let [bc (count b-vec) xc (count x-vec)]
    (loop [n 0 sig x-vec result []]
        (if (= n (- xc bc))
          result
          (recur (inc n) (rest sig) (conj result (->> sig
                                                  (take bc)
                                                  (reverse)
                                                  (map * b-vec)
                                                  (apply +)))))))) 


(time (def b (filt [1 2 3] (range 10000))))
=> "Elapsed time: 50.892536 msecs"

答案 1 :(得分:6)

如果你真的想要这种计算的最佳性能,你应该使用数组而不是矢量。阵列具有许多性能优势:

  • 它们支持O(1)索引查找和写入 - 比O(log32 n)
  • 的向量略胜一筹
  • 它们是可变的,因此您不需要一直构建新数组 - 您只需创建一个数组作为输出缓冲区
  • 它们被表示为Java数组,因此受益于JVM中内置的各种数组优化
  • 您可以使用比使用盒装数字对象快得多的原始数组(例如Java双打)

代码如下:

(defn filt [^doubles b-arr 
            ^doubles x-arr]
     (let [bc (count b-arr) 
           xc (count x-arr)
           rc (inc (- xc bc))
           result ^doubles (double-array rc)]
       (dotimes [i rc]
         (dotimes [j bc]
           (aset result i (+ (aget result i) (* (aget x-arr (+ i j)) (aget b-arr j))))))
       result))

答案 2 :(得分:3)

要继续使用Ankur的优秀答案,您还可以避免重复调用反向功能,这样可以让我们获得更高的性能。

(defn filt [b-vec x-vec]
  (let [bc (count b-vec) xc (count x-vec) bb-vec (reverse b-vec)]
    (loop [n 0 sig x-vec result []]
        (if (= n (- xc bc))
          result
          (recur (inc n) (rest sig) (conj result (->> sig
                                                  (take bc)
                                                  (map * bb-vec)
                                                  (apply +))))))))