From 387d817e9bea785dff3a73090ab38084c082b79d Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Mon, 25 May 2020 12:11:06 +0100 Subject: [PATCH] Investigation of Dali performance issue --- doc/Dali-performance.ods | Bin 0 -> 9511 bytes doc/dali-performance.md | 148 +++++++++++++++++++++++++++++++++++++++ project.clj | 3 +- src/walkmap/core.clj | 41 +---------- src/walkmap/ocean.clj | 24 +++++++ src/walkmap/svg.clj | 85 ++++++++++++++++++---- 6 files changed, 250 insertions(+), 51 deletions(-) create mode 100644 doc/Dali-performance.ods create mode 100644 doc/dali-performance.md create mode 100644 src/walkmap/ocean.clj diff --git a/doc/Dali-performance.ods b/doc/Dali-performance.ods new file mode 100644 index 0000000000000000000000000000000000000000..f05e886864b85f7abaf5c2e25a611a3eb1a8d00a GIT binary patch literal 9511 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 M@(^^2$R6+h4?f5Xi2wiq literal 0 HcmV?d00001 diff --git a/doc/dali-performance.md b/doc/dali-performance.md new file mode 100644 index 0000000..e407362 --- /dev/null +++ b/doc/dali-performance.md @@ -0,0 +1,148 @@ +# Dali performance + +Notes written while trying to characterise the performance problem in Dali. + +## Hypothesis one: it's the way I format the polygons that's the issue + +Firstly, with both versions of `stl->svg` using the same version of `facet->svg-poly`, i.e. this one: + + (defn- facet->svg-poly + [facet] + [:polygon + {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}]) + +we get this performance using the smaller `isle_of_man` map: + + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg"))) + 20-05-25 09:21:43 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer + 20-05-25 09:21:43 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer + "Elapsed time: 86.904891 msecs" + #'walkmap.svg/hiccup + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg"))) + 20-05-25 09:22:17 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer + 20-05-25 09:22:17 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer + "Elapsed time: 890.863814 msecs" + #'walkmap.svg/dali + +If we switch the Dali render to use my original version of `facet->svg-poly`, i.e. this one: + + (defn- dali-facet->svg-poly + [facet] + (vec + (cons + :polygon + (map #(vec (list (:x %) (:y %))) (:vertices facet))))) + +we get this performance: + + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg"))) + 20-05-25 09:35:33 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer + 20-05-25 09:35:33 mason INFO [walkmap.svg:96] - Emitting SVG with :hiccup renderer + "Elapsed time: 84.09972 msecs" + #'walkmap.svg/hiccup + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl" "resources/isle_of_man.svg"))) + 20-05-25 09:35:41 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer + 20-05-25 09:35:41 mason INFO [walkmap.svg:96] - Emitting SVG with :dali renderer + "Elapsed time: 874.292007 msecs" + #'walkmap.svg/dali + +No significant difference in performance. + +If we generate but don't render, we get this: + + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def hiccup (binary-stl-file->svg "resources/isle_of_man.stl"))) + 20-05-25 09:37:44 mason INFO [walkmap.svg:82] - Generating SVG for :hiccup renderer + "Elapsed time: 52.614707 msecs" + #'walkmap.svg/hiccup + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (def dali (binary-stl-file->svg "resources/isle_of_man.stl"))) + 20-05-25 09:38:07 mason INFO [walkmap.svg:82] - Generating SVG for :dali renderer + "Elapsed time: 49.891043 msecs" + #'walkmap.svg/dali + +This implies that the problem is not in the way polygons are formatted. + +The difference between the two versions of `facet->svg-poly` is as follows: + +### New version, works with both Hiccup and Dali: + + walkmap.svg=> (def stl (decode-binary-stl "resources/isle_of_man.stl")) + #'walkmap.svg/stl + walkmap.svg=> (def facet (first (:facets stl))) + #'walkmap.svg/facet + walkmap.svg=> (pprint facet) + {:normal {:x -0.0, :y 0.0, :z 1.0}, + :vertices + [{:x 3.0, :y 1.0, :z 1.0} + {:x 2.0, :y 3.0, :z 1.0} + {:x 0.0, :y 0.0, :z 1.0}], + :abc 0} + nil + walkmap.svg=> (pprint (facet->svg-poly facet)) + [:polygon {:points "3.0,1.0 2.0,3.0 0.0,0.0"}] + nil + +In other words, the new version constructs the `:points` attribute of the `:polygon` tag by string concatenation, and the renderer just needs to output it. + +### Older version, works with Dali only: + + walkmap.svg=> (pprint (dali-facet->svg-poly facet)) + [:polygon [3.0 1.0] [2.0 3.0] [0.0 0.0]] + nil + +This means that the renderer is actually doing more work, since it has to compose the `:points` attribute itself; nevertheless there doesn't seem to be an increased time penalty. + +### Conclusion + +It doesn't seem that formatting the polygons is the issue. + +## Hypothesis two: Dali renderer scales non-linearly with number of objects drawn + +To test this, we need some otherwise-similar test files with different numbers of objects: + + walkmap.svg=> (count (:facets stl)) + 4416 + walkmap.svg=> (def small-stl (assoc stl :facets (take 400 (:facets stl)))) + #'walkmap.svg/small-stl + walkmap.svg=> (count (:facets small-stl)) + 400 + walkmap.svg=> (def large-stl (decode-binary-stl "../the-great-game/resources/maps/heightmap.stl")) + #'walkmap.svg/large-stl + walkmap.svg=> (count (:facets large-stl)) + 746585 + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :dali) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (dali.io/render-svg (stl->svg small-stl) "dali-small.svg")) + 20-05-25 10:12:25 mason INFO [walkmap.svg:92] - Generating SVG for :dali renderer + "Elapsed time: 32.55506 msecs" + nil + walkmap.svg=> (def ^:dynamic *preferred-svg-render* :hiccup) + #'walkmap.svg/*preferred-svg-render* + walkmap.svg=> (time (spit "hiccup-small.svg" (hiccup.core/html (stl->svg small-stl)))) + 20-05-25 10:14:07 mason INFO [walkmap.svg:92] - Generating SVG for :hiccup renderer + "Elapsed time: 10.026369 msecs" + +So we have + +| | Dali | | Hiccup | | | +| ----------- | ---------------- | ----------- | ------------ | ----------- | ------------------- | +| # of facets | time (msecs) | objets/msec | time (msecs) | objets/msec | ratio (Dali/Hiccup) | +| ----------- | ---------------- | ----------- | ------------ | ----------- | --------------------| +| 400 | 32.55506 | 12.29 | 10.026369 | 39.89 | 3.35 | +| 4416 | 874.292007 | 5.05 | 84.09972 | 52.51 | 10.40 | +| 746585 | 29,695,695.61 | 0.03 | 16724.848222 | 44.64 | 1775.54 | + +### Conclusion + +What we're seeing is that Hiccup renders more or less linearly by the number of objects (bear in mind that all of these objects are triangles, so essentially equally complex to render), whereas trhe performance of Dali degrades significantly as the number of objects increases. diff --git a/project.clj b/project.clj index 72a0635..48b6db4 100644 --- a/project.clj +++ b/project.clj @@ -18,7 +18,8 @@ :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} :plugins [[lein-cloverage "1.1.1"] - [lein-codox "0.10.7"]] + [lein-codox "0.10.7"] + [lein-gorilla "0.4.0"]] :release-tasks [["vcs" "assert-committed"] ["change" "version" "leiningen.release/bump-version" "release"] ["vcs" "commit"] diff --git a/src/walkmap/core.clj b/src/walkmap/core.clj index 09fcc30..3499a93 100644 --- a/src/walkmap/core.clj +++ b/src/walkmap/core.clj @@ -1,44 +1,9 @@ (ns walkmap.core - "At this stage, primarily utility functions dealing with stereolithography - (STL) files. Not a stable API yet!" + "This namespace mostly gets used as a scratchpad for ideas which haven't yet + solidified." (:require [clojure.java.io :as io :refer [file output-stream input-stream]] [clojure.string :as s] [hiccup.core :refer [html]] [me.raynes.fs :as fs] - [taoensso.timbre :as l :refer [info error spy]] - [walkmap.stl :refer [decode-binary-stl]] - [walkmap.svg :refer [stl->svg]])) - -(def ^:dynamic *sea-level* - "The sea level on heightmaps we're currently handling. If characters are to - be able to swin in the sea, we must model the sea bottom, so we need - heightmaps which cover at least the continental shelf. However, the sea - bottom is not walkable territory and can be culled from walkmaps. - - **Note** must be a floating point number. `(= 0 0.0)` returns `false`!" - 0.0) - -(defn ocean? - "Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?" - [facet] - (every? - #(= % *sea-level*) - (map :z (:vertices facet)))) - -(defn cull-ocean-facets - "Ye cannae walk on water. Remove all facets from this `stl` structure which - are at sea level." - [stl] - (assoc stl :facets (remove ocean? (:facets stl)))) - -(defn binary-stl-file->svg - "Given only an `in-filename`, parse the indicated file, expected to be - binary STL, and return an equivalent SVG structure. Given both `in-filename` - and `out-filename`, as side-effect write the SVG to the indicated output file." - ([in-filename] - (stl->svg (cull-ocean-facets (decode-binary-stl in-filename)))) - ([in-filename out-filename] - (let [s (binary-stl-file->svg in-filename)] - (spit out-filename (html s)) - s))) + [taoensso.timbre :as l :refer [info error spy]])) diff --git a/src/walkmap/ocean.clj b/src/walkmap/ocean.clj new file mode 100644 index 0000000..44abd35 --- /dev/null +++ b/src/walkmap/ocean.clj @@ -0,0 +1,24 @@ +(ns walkmap.ocean + "Deal with (specifically, at this stage, cull) ocean areas") + +(def ^:dynamic *sea-level* + "The sea level on heightmaps we're currently handling. If characters are to + be able to swin in the sea, we must model the sea bottom, so we need + heightmaps which cover at least the continental shelf. However, the sea + bottom is not walkable territory and can be culled from walkmaps. + + **Note** must be a floating point number. `(= 0 0.0)` returns `false`!" + 0.0) + +(defn ocean? + "Of a `facet`, is the altitude of every vertice equal to `*sea-level*`?" + [facet] + (every? + #(= % *sea-level*) + (map :z (:vertices facet)))) + +(defn cull-ocean-facets + "Ye cannae walk on water. Remove all facets from this `stl` structure which + are at sea level." + [stl] + (assoc stl :facets (remove ocean? (:facets stl)))) diff --git a/src/walkmap/svg.clj b/src/walkmap/svg.clj index 5b5324c..5841a42 100644 --- a/src/walkmap/svg.clj +++ b/src/walkmap/svg.clj @@ -3,15 +3,68 @@ later, other geometry files of interest to us) as scalable vector graphics (SVG)." (:require [clojure.string :as s] + [dali.io :as neatly-folded-clock] + [hiccup.core :refer [html]] [taoensso.timbre :as l :refer [info error spy]] + [walkmap.ocean :refer [cull-ocean-facets]] [walkmap.polygon :refer [polygon?]] + [walkmap.stl :refer [decode-binary-stl]] [walkmap.vertex :refer [vertex?]])) +(def ^:dynamic *preferred-svg-render* + "Mainly for debugging dali; switch SVG renderer to use. Expected values: + `:dali`, `:hiccup`." + :dali) + (defn- facet->svg-poly + ;; When we use this version of facet->svg-poly with the Dali renderer, it's + ;; still (for the isle_of_man map) 10 times slower than hiccup, also using + ;; this version (890.863814 msecs vs 86.904891 msecs [facet] [:polygon {:points (s/join " " (map #(str (:x %) "," (:y %)) (:vertices facet)))}]) +(defn- dali-facet->svg-poly + [facet] + (vec + (cons + :polygon + (map #(vec (list (:x %) (:y %))) (:vertices facet))))) + +(defn dali-stl->svg + "Format this `stl` as SVG for the `hiccup` renderer on a page with these + bounds." + [stl minx maxx miny maxy] + [:dali/page + {:xmlns "http://www.w3.org/2000/svg" + :version "1.2" + :width (- maxx minx) + :height (- maxy miny) + :viewBox (s/join " " (map str [minx miny maxx maxy]))} + (vec + (cons + :g + (map + dali-facet->svg-poly + (:facets stl))))]) + +(defn hiccup-stl->svg + "Format this `stl` as SVG for the `hiccup` renderer on a page with these + bounds." + [stl minx maxx miny maxy] + [:svg + {:xmlns "http://www.w3.org/2000/svg" + :version "1.2" + :width (- maxx minx) + :height (- maxy miny) + :viewBox (s/join " " (map str [minx miny maxx maxy]))} + (vec + (cons + :g + (map + facet->svg-poly + (:facets stl))))]) + (defn stl->svg "Convert this in-memory `stl` structure, as read by `decode-binary-stl`, into an in-memory hiccup representation of SVG structure, and return it." @@ -36,15 +89,23 @@ (map #(reduce max (map :y (:vertices %))) (:facets stl)))] - [:svg - {:xmlns "http://www.w3.org/2000/svg" - :version "1.2" - :width (- maxx minx) - :height (- maxy miny) - :viewBox (s/join " " (map str [minx miny maxx maxy]))} - (vec - (cons - :g - (map - facet->svg-poly - (:facets stl))))])) + (l/info "Generating SVG for " *preferred-svg-render* " renderer") + (case *preferred-svg-render* + :hiccup (hiccup-stl->svg stl minx maxx miny maxy) + :dali (dali-stl->svg stl minx maxx miny maxy) + (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*))))) + +(defn binary-stl-file->svg + "Given only an `in-filename`, parse the indicated file, expected to be + binary STL, and return an equivalent SVG structure. Given both `in-filename` + and `out-filename`, as side-effect write the SVG to the indicated output file." + ([in-filename] + (stl->svg (cull-ocean-facets (decode-binary-stl in-filename)))) + ([in-filename out-filename] + (let [s (binary-stl-file->svg in-filename)] + (l/info "Emitting SVG with " *preferred-svg-render* " renderer") + (case *preferred-svg-render* + :dali (neatly-folded-clock/render-svg s out-filename) + :hiccup (spit out-filename (html s)) + (throw (Exception. "Unexpected renderer value: " *preferred-svg-render*))) + s)))