2012-03-01 246 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)) .....] .....)等。這樣做的主要原因是避免了盒裝原語的開銷,這又意味着你想避免在內部循環中分配內存。