什么是clojure.lang.Var.getRawRoot,为什么要调用它?

时间:2012-03-01 09:52:01

标签: optimization clojure

我正在编写一个函数来检查两个点是否可以在2D网格上相互看到路径寻找算法。在分析代码之后,我发现它花了60%的时间在clojure.lang.Var.getRawRoot()中。为什么这个功能消耗了这么多时间,我可以优化它吗?

(defn line-of-sight-helper [^Maze maze [x0 y0] [x1 y1]]
  "Determines if there is a line of sight from [x0 y0] to [x1 y1] in maze."
  (let [dy (int (- y1 y0))
        dx (int (- x1 x0))
        sy (int (if (neg? dy) -1 1))
        sx (int (if (neg? dx) -1 1))
        dy (int (* sy dy))
        dx (int (* sx dx))
        bias-x (int (if (pos? sx) 0 -1))
        bias-y (int (if (pos? sy) 0 -1))
        x-long (boolean (>= dx dy))
        [u0 u1 du su bias-u] (if x-long
                               [(int x0) (int x1) dx sx bias-x]
                               [(int y0) (int y1) dy sy bias-y])
        [v0 v1 dv sv bias-v] (if x-long
                               [(int y0) (int y1) dy sy bias-y]
                               [(int x0) (int x1) dx sx bias-x])
        grid (if x-long
               #(blocked? maze [%1 %2])
               #(blocked? maze [%2 %1]))]
    (loop [u0 u0
             v0 v0
             error (int 0)]
      (if (not= u0 u1)
        (let [error (+ error dv)
              too-much-error? (> error du)
              next-blocked? (grid (+ u0 bias-u) (+ v0 bias-v))
              branch3 (and too-much-error? (not next-blocked?))
              v0 (int (if branch3
                        (+ v0 sv)
                        v0))
              error (if branch3
                      (int (- error du))
                      (int error))]
          (if (and too-much-error? next-blocked?)
            false
            (if (and (not (zero? error)) next-blocked?)
              false
              (if (and (zero? dv)
                       (grid (+ u0 bias-u)
                             v0)
                       (grid (+ u0 bias-u)
                             (- v0 1)))
                false
                (recur (int (+ u0 su))
                       v0
                       error)))))
       true))))

1 个答案:

答案 0 :(得分:4)

getVarRoot发生了什么?

我真的很惊讶任何程序都花了很多时间在getRawRoot()上。所有这个方法都是从Var返回一个字段,根据clojure.lang.Var中的来源:

final public Object getRawRoot(){
    return root;
}

另外,它是一个小的final方法,所以应该由任何现代JIT编译器内联......基本上任何对getRawRoot的调用都应该非常快。

我怀疑你的探查器发生了一些奇怪的事情:也许是在getRawRoot()中添加调试代码需要花费很多时间。因此,我建议在没有探查器的情况下对代码进行基准测试,并使用java -server来查看函数的真正执行情况。

其他效果提示

确保使用 Clojure 1.3 + ,因为对var访问有一些优化,你几乎肯定会想要在这种低级代码中利用它。

如果我猜测这段代码中实际上最大的瓶颈是什么,那么我认为网格函数#(blocked? maze [%1 %2]) 构造一个新的向量每次调用它来检查网格方块。如果你能重构它以便它不需要一个向量会更好,那么你可以直接使用#(blocked? maze %1 %2)。与简单的数学运算相比,构建新集合的成本很高,因此您希望在内部循环中谨慎使用它。

您还希望确保尽可能使用原始操作,并使用(set! *unchecked-math* true)。确保将当地人声明为原语,因此您需要例如(let [u0 (int (if x-long x0 y0)) .....] .....)等。这样做的主要原因是避免了盒装基元的开销,这再次暗示了你想要在内部循环中避免的内存分配。