What is clojure.lang.Var.getRawRoot and why is it called?

I wrote a function that checks if two points can see each other in a 2D grid for a path search algorithm. After profiling the code, I found that he spent 60% of his time in clojure.lang.Var.getRawRoot (). Why does this function consume so much time and can I optimize it?

(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
source share
1 answer

What happens with getVarRoot?

I am really surprised that any program spends a lot of time on getRawRoot (). This whole method returns a single field from Var, according to the source in clojure.lang.Var :

 final public Object getRawRoot(){ return root; } 

In addition, this is a small final method, so it should be built in by any modern JIT compiler ..... basically any getRawRoot calls should be insanely fast.

I suspect something strange is happening with your profiler: it may be adding debugging code to getRawRoot (), which takes a lot of time. Therefore, I suggest comparing your code without a profiler and java -server to see how the function really works.

Other performance tips

Make sure you use Clojure 1.3 + , as there are some optimizations for accessing var that you will almost certainly want to use in this low-level code.

If I thought about what is actually the biggest bottleneck in this code, I think it will be the fact that the grid #(blocked? maze [%1 %2]) function #(blocked? maze [%1 %2]) constructs a new vector each time called to check the square of the grid. It would be much better if you could reorganize it so that it does not need a vector, and you could just use #(blocked? maze %1 %2) directly. Building new collections is expensive compared to simple math operations, so you want to do it sparingly in your internal loops.

You must also make sure that you use primitive operations where possible and with (set! *unchecked-math* true) . Make sure you declare your local residents as primitives, so you want, for example. (let [u0 (int (if x-long x0 y0)) .....] .....) etc. The main reason for this is to avoid the overhead of primitives with boxes, which again involves allocating memory that you want to avoid in internal loops.

+4
source

Source: https://habr.com/ru/post/1399150/


All Articles