2012-03-01 206 views
4

我正在写一个函数,用于检查两个点在寻路算法的二维网格上是否可以看到彼此。在分析代码后,我发现它的时间花费了60%的时间在clojure.lang.Var.getRawRoot()。为什么这个函数消耗了这么多时间,我可以优化它吗?什么是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)))) 

回答

4

getVarRoot发生了什么?

我真的很惊讶,任何程序花费了很多时间getRawRoot()。所有这些方法确实是从瓦尔在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)) .....] .....)等。这样做的主要原因是避免了盒装原语的开销,这又意味着你想避免在内部循环中分配内存。