在Clojure中调试执行缓慢的功能

时间:2018-08-10 03:50:27

标签: clojure clojure-core.logic

我正在尝试为在clojure中对数组进行排序所需的最小交换实现一种解决方案。

该代码可以工作,但是要花费7秒钟才能解决7个元素的向量,与Java中的类似解决方案相比,它的性能非常差。 (编辑) 我已经尝试提供显式类型,但是似乎没有什么不同 我尝试使用瞬态,但是在解决方案中使用的是subvec的一个开放式错误-https://dev.clojure.org/jira/browse/CLJ-787

关于如何优化解决方案的任何指示?

;; Find minimumSwaps required to sort the array. The algorithm, starts by iterating from 0 to n-1. In each iteration, it places the least element in the ith position. 

(defn minimumSwaps [input]
  (loop [mv input, i (long 0), swap-count (long 0)]
    (if (< i (count input))
       (let [min-elem (apply min (drop i mv))]
        (if (not= min-elem (mv i))
          (recur (swap-arr  mv i min-elem),
                 (unchecked-inc i),
                 (unchecked-inc swap-count))
          (recur mv,
                 (unchecked-inc i),
                 swap-count)))
      swap-count)))

(defn swap-arr [vec x min-elem]
  (let [y (long (.indexOf vec min-elem))]
    (assoc vec x (vec y) y (vec x))))

(time (println (minimumSwaps [7 6 5 4 3 2 1])))

1 个答案:

答案 0 :(得分:0)

在算法和效率方面,您可以在解决方案中进行一些改进。主要的改进是在搜索时既要记住向量中的最小元素又要记住它的位置。这样一来,您就不必再使用.indexOf搜索最小元素了。

这是我修改后的解决方案,速度快了约4倍:

(defn swap-arr [v x y]
  (assoc v x (v y) y (v x)))

(defn find-min-and-position-in-vector [v, ^long start-from]
  (let [size (count v)]
    (loop [i start-from, min-so-far (long (nth v start-from)), min-pos start-from]
      (if (< i size)
        (let [x (long (nth v i))]
          (if (< x min-so-far)
            (recur (inc i) x i)
            (recur (inc i) min-so-far min-pos)))
        [min-so-far min-pos]))))

(defn minimumSwaps [input]
  (loop [mv input, i (long 0), swap-count (long 0)]
    (if (< i (count input))
      (let [[min-elem min-pos] (find-min-and-position-in-vector mv i)]
        (if (not= min-elem (mv i))
          (recur (swap-arr mv i min-pos),
                 (inc i),
                 (inc swap-count))
          (recur mv,
                 (inc i),
                 swap-count)))
      swap-count)))

要了解程序中的性能瓶颈在哪里,最好使用https://github.com/clojure-goes-fast/clj-async-profiler而不是猜测。

请注意我是如何从您的代码中删除unchecked-*东西的。它在这里不是那么重要,很容易弄错。如果要使用它们来提高性能,请确保使用反编译器检查生成的字节码:https://github.com/clojure-goes-fast/clj-java-decompiler

  

Java中类似的实现,几乎运行一半的时间。

对于Clojure来说,这实际上是相当不错的,因为您使用了不可变的向量,而在Java中您可能使用了数组。将Clojure解决方案重写为数组后,性能几乎相同。