Skip to content

Commit

Permalink
Fixed memory leak + related simplifications
Browse files Browse the repository at this point in the history
Removed grid-based neighbor updating system.
Eliminated use of reducers.
  • Loading branch information
lspector committed Sep 26, 2014
1 parent 03259ba commit 4824218
Show file tree
Hide file tree
Showing 16 changed files with 102 additions and 208 deletions.
29 changes: 29 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>pucks</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>ccw.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>ccw.leiningen.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>ccw.leiningen.nature</nature>
<nature>ccw.nature</nature>
</natures>
</projectDescription>
2 changes: 1 addition & 1 deletion bin/pucks/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
[]
(when (not @paused)
(swap! iteration inc)
(swap! world-objects (fn [objs] (map #(assoc % :steps (inc (:steps %))) objs))) ;; update step clocks in agents
(swap! world-objects (fn [objs] (mapv #(assoc % :steps (inc (:steps %))) objs))) ;; update step clocks in agents
(update-neighbors)
(run-sensors)
(generate-proposals) ;; should access only :sensed, not :neighbors (still needed for arbitration) and not :position
Expand Down
76 changes: 16 additions & 60 deletions bin/pucks/neighbors.clj
Original file line number Diff line number Diff line change
@@ -1,77 +1,33 @@
(ns pucks.neighbors
(:use pucks.globals pucks.util pucks.vec2D))

(defn wrap-grid-coordinate
[c]
(if (< c 0)
(dec (:grid-steps @parameters))
(if (>= c (:grid-steps @parameters))
0
c)))

(defn wrap-gridxy
[xy]
(vec (map wrap-grid-coordinate xy)))

(defn xy->gridxy
[xy]
(wrap-gridxy (map (fn [c] (int (/ c (:grid-step-size @parameters)))) xy)))

(defn strip-neighbors
[obj]
(-> obj
(dissoc :neighbors)
(dissoc :sensed)
(dissoc :overlaps)))

(defn make-world-grid
[objs]
(let [grid-steps (max 1 (int (/ (:screen-size @parameters)
(:neighborhood-size @parameters))))]
(swap! parameters
(fn [params]
(-> params
(assoc :grid-steps grid-steps)
(assoc :grid-step-size (max 1 (int (/ (:screen-size params)
grid-steps)))))))
(loop [grid (vec (repeat grid-steps (vec (repeat grid-steps []))))
remaining (into [] (map strip-neighbors objs))]
(if (empty? remaining)
grid
(recur (update-in grid
(xy->gridxy (:position (first remaining)))
conj
(first remaining))
(rest remaining))))))

(defn update-neighbors
"Annotates each world object with :neighbors and :overlaps."
[]
(swap!
world-objects
(fn [objs]
(let [grid (make-world-grid objs)
window (fn [c] (mapv #(wrap-grid-coordinate (+ c %)) [-1 0 1]))]
(into []
(pmapall (fn [obj]
(let [neighs (vec
(relativize-positions
(filter #(and (not (= (:id obj) (:id %)))
(<= (length (map - (:position obj) (:position %)))
(:neighborhood-size @parameters)))
(apply concat
(let [[x y] (xy->gridxy (:position obj))]
(doall
(for [xs (window x)
ys (window y)]
(get-in grid (wrap-gridxy [xs ys])))))))
(:position obj)))]
(-> obj
(assoc :neighbors neighs)
(assoc :overlaps
(vec (filter #(<= (length (:position %))
(+ (:radius obj) (:radius %)))
neighs))))))
objs))))))

(let [stripped (mapv strip-neighbors objs)]
(pmapallv (fn [obj]
(let [neighs (vec
(relativize-positions
(filterv #(and (not (= (:id obj) (:id %)))
(<= (length (mapv - (:position obj) (:position %)))
(:neighborhood-size @parameters)))
stripped)
(:position obj)))]
(-> obj
(assoc :neighbors neighs)
(assoc :overlaps
(filterv #(<= (length (:position %))
(+ (:radius obj) (:radius %)))
neighs)))))
stripped)))))

2 changes: 1 addition & 1 deletion bin/pucks/physics.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(swap! world-objects
(fn [objs]
(into []
(pmapall #(assoc % :proposals ((:proposal-function %) %))
(pmapallv #(assoc % :proposals ((:proposal-function %) %))
objs)))))

(defn colliding?
Expand Down
5 changes: 1 addition & 4 deletions bin/pucks/sensors.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
(ns pucks.sensors
(:require [clojure.core.reducers :as r])
(:use [pucks globals util vec2D]))

(defn sense
Expand All @@ -21,6 +20,4 @@ within sensor range."
[]
(swap! world-objects
(fn [objs]
(into [] (r/fold 16 r/cat r/append! (r/map sense objs))))))


(pmapallv sense objs))))
43 changes: 11 additions & 32 deletions bin/pucks/util.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
(ns pucks.util
(:require [clojure.core.reducers :as r])
(:use clojure.repl clojure.pprint quil.core pucks.globals pucks.vec2D))

(defn wrap-rotation
Expand Down Expand Up @@ -69,37 +68,17 @@ to (:screen-size parameters) (exclusive)."
(when (zero? (mod @iteration 100))
(println)
(pprint (first @world-objects))))


#_(defn pmapall
"Like pmap but: 1) coll should be finite, 2) the returned sequence
will not be lazy, 3) calls to f may occur in any order, to maximize
multicore processor utilization, and 4) takes only one coll so far."
[f coll]
(if (:single-thread-mode @parameters)
(doall (map f coll))
(let [agents (map #(agent % :error-handler
(fn [agnt except] (clojure.repl/pst except 1000) (System/exit 0)))
coll)]
(dorun (map #(send % f) agents))
(apply await agents)
(doall (map deref agents)))))

(defn pmapall
"Like pmap but: 1) coll should be finite, 2) the returned sequence
will not be lazy, 3) calls to f may occur in any order, to maximize
multicore processor utilization, and 4) takes only one coll so far."
[f coll]
(if (:single-thread-mode @parameters)
(doall (map f coll))
(r/fold 1 r/cat r/append! (r/map f coll))))

(defn pmapallv
"Like pmap but: 1) coll should be finite, 2) the returned sequence
will be a vector, 3) calls to f may occur in any order, to maximize
"Like pmap but: 1) coll should be finite, 2) the returned sequence
will not be lazy, 3) calls to f may occur in any order, to maximize
multicore processor utilization, and 4) takes only one coll so far."
[f coll]
(into []
(if (:single-thread-mode @parameters)
(doall (map f coll))
(r/fold 1 r/cat r/append! (r/map f coll)))))
[f coll]
(vec (if (:single-thread-mode @parameters)
(doall (map f coll))
(let [agents (map #(agent % :error-handler
(fn [agnt except] (clojure.repl/pst except 1000) (System/exit 0)))
coll)]
(dorun (map #(send % f) agents))
(apply await agents)
(doall (map deref agents))))))
2 changes: 1 addition & 1 deletion bin/pucks/worlds/dev/world1.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(ns pucks.worlds.dev.world1
(:use [pucks core globals]
[pucks.agents nursery linear stone vent zapper swarmer beacon]))
[pucks.agents nursery linear stone vent zapper swarmer beacon]))

(defn agents []
(concat (repeatedly 10 stone)
Expand Down
17 changes: 9 additions & 8 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject pucks "0.1.0-SNAPSHOT"
(defproject pucks "0.1.1"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
Expand All @@ -9,10 +9,11 @@
:target-path "target/%s"
:profiles {:uberjar {:aot :all}}
;; the following should automatically take n% of the machine's RAM and also use the G1 garbage collector
:jvm-opts ~(let [mem-to-use (long (* (.getTotalPhysicalMemorySize
(java.lang.management.ManagementFactory/getOperatingSystemMXBean))
0.5))]
[(str "-Xmx" mem-to-use)
(str "-Xms" mem-to-use)
;"-XX:+UseG1GC"
]))
;:jvm-opts ~(let [mem-to-use (long (* (.getTotalPhysicalMemorySize
; (java.lang.management.ManagementFactory/getOperatingSystemMXBean))
; 0.5))]
; [(str "-Xmx" mem-to-use)
; (str "-Xms" mem-to-use)
; ;"-XX:+UseG1GC"
; ])
)
2 changes: 1 addition & 1 deletion src/pucks/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
[]
(when (not @paused)
(swap! iteration inc)
(swap! world-objects (fn [objs] (map #(assoc % :steps (inc (:steps %))) objs))) ;; update step clocks in agents
(swap! world-objects (fn [objs] (mapv #(assoc % :steps (inc (:steps %))) objs))) ;; update step clocks in agents
(update-neighbors)
(run-sensors)
(generate-proposals) ;; should access only :sensed, not :neighbors (still needed for arbitration) and not :position
Expand Down
76 changes: 16 additions & 60 deletions src/pucks/neighbors.clj
Original file line number Diff line number Diff line change
@@ -1,77 +1,33 @@
(ns pucks.neighbors
(:use pucks.globals pucks.util pucks.vec2D))

(defn wrap-grid-coordinate
[c]
(if (< c 0)
(dec (:grid-steps @parameters))
(if (>= c (:grid-steps @parameters))
0
c)))

(defn wrap-gridxy
[xy]
(vec (map wrap-grid-coordinate xy)))

(defn xy->gridxy
[xy]
(wrap-gridxy (map (fn [c] (int (/ c (:grid-step-size @parameters)))) xy)))

(defn strip-neighbors
[obj]
(-> obj
(dissoc :neighbors)
(dissoc :sensed)
(dissoc :overlaps)))

(defn make-world-grid
[objs]
(let [grid-steps (max 1 (int (/ (:screen-size @parameters)
(:neighborhood-size @parameters))))]
(swap! parameters
(fn [params]
(-> params
(assoc :grid-steps grid-steps)
(assoc :grid-step-size (max 1 (int (/ (:screen-size params)
grid-steps)))))))
(loop [grid (vec (repeat grid-steps (vec (repeat grid-steps []))))
remaining (into [] (map strip-neighbors objs))]
(if (empty? remaining)
grid
(recur (update-in grid
(xy->gridxy (:position (first remaining)))
conj
(first remaining))
(rest remaining))))))

(defn update-neighbors
"Annotates each world object with :neighbors and :overlaps."
[]
(swap!
world-objects
(fn [objs]
(let [grid (make-world-grid objs)
window (fn [c] (mapv #(wrap-grid-coordinate (+ c %)) [-1 0 1]))]
(into []
(pmapall (fn [obj]
(let [neighs (vec
(relativize-positions
(filter #(and (not (= (:id obj) (:id %)))
(<= (length (map - (:position obj) (:position %)))
(:neighborhood-size @parameters)))
(apply concat
(let [[x y] (xy->gridxy (:position obj))]
(doall
(for [xs (window x)
ys (window y)]
(get-in grid (wrap-gridxy [xs ys])))))))
(:position obj)))]
(-> obj
(assoc :neighbors neighs)
(assoc :overlaps
(vec (filter #(<= (length (:position %))
(+ (:radius obj) (:radius %)))
neighs))))))
objs))))))

(let [stripped (mapv strip-neighbors objs)]
(pmapallv (fn [obj]
(let [neighs (vec
(relativize-positions
(filterv #(and (not (= (:id obj) (:id %)))
(<= (length (mapv - (:position obj) (:position %)))
(:neighborhood-size @parameters)))
stripped)
(:position obj)))]
(-> obj
(assoc :neighbors neighs)
(assoc :overlaps
(filterv #(<= (length (:position %))
(+ (:radius obj) (:radius %)))
neighs)))))
stripped)))))

2 changes: 1 addition & 1 deletion src/pucks/physics.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(swap! world-objects
(fn [objs]
(into []
(pmapall #(assoc % :proposals ((:proposal-function %) %))
(pmapallv #(assoc % :proposals ((:proposal-function %) %))
objs)))))

(defn colliding?
Expand Down
5 changes: 1 addition & 4 deletions src/pucks/sensors.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
(ns pucks.sensors
(:require [clojure.core.reducers :as r])
(:use [pucks globals util vec2D]))

(defn sense
Expand All @@ -21,6 +20,4 @@ within sensor range."
[]
(swap! world-objects
(fn [objs]
(into [] (r/fold 16 r/cat r/append! (r/map sense objs))))))


(pmapallv sense objs))))
Loading

0 comments on commit 4824218

Please sign in to comment.