git.iko.soy/typeable/elm-ui
on master help sign in
add render benchmark results view
8150131Matthew Griffith
benchmarks/elm.json+11−2
@@ -7,15 +7,24 @@
7 "elm-version": "0.19.0",
7 "elm-version": "0.19.0",
8 "dependencies": {
8 "dependencies": {
9 "direct": {
9 "direct": {
10+ "avh4/elm-color": "1.0.0",
10 "elm/browser": "1.0.1",
11 "elm/browser": "1.0.1",
11 "elm/core": "1.0.2",
12 "elm/core": "1.0.2",
12 "elm/html": "1.0.0",
13 "elm/html": "1.0.0",
13 "elm/json": "1.1.3",
14 "elm/json": "1.1.3",
14 "elm/time": "1.0.0",
15 "elm/time": "1.0.0",
15− "elm/virtual-dom": "1.0.2"
16+ "elm/virtual-dom": "1.0.2",
17+ "terezka/line-charts": "2.0.0"
16 },
18 },
17 "indirect": {
19 "indirect": {
20+ "debois/elm-dom": "1.3.0",
21+ "elm/parser": "1.1.0",
22+ "elm/svg": "1.0.1",
18− "elm/url": "1.0.0"
23+ "elm/url": "1.0.0",
24+ "justinmimbs/date": "3.1.2",
25+ "justinmimbs/time-extra": "1.1.0",
26+ "myrho/elm-round": "1.0.4",
27+ "ryannhg/date-format": "2.3.0"
19 }
28 }
20 },
29 },
21 "test-dependencies": {
30 "test-dependencies": {
benchmarks/runtime/index.js+78−33
@@ -15,36 +15,79 @@ main = Benchmark.Render.toProgram ${item.module}.${item.value}
15 fs.writeFileSync('./tmp/Main.elm', entrypoint);
15 fs.writeFileSync('./tmp/Main.elm', entrypoint);
16}
16}
17
17
18+
19+function regoupResults(results) {
20+ var regrouped = {}
21+
22+ for (var i = 0; i < results.length; i++) {
23+ if (results[i].group) {
24+
25+ var reorged = {
26+ count: results[i].count
27+ , fps: results[i].frames.fps
28+ , timeToFirstPaintMS: results[i].perf.TimeToFirstPaintMS
29+ , nodes: results[i].perf.Nodes
30+ , layoutSeconds: results[i].perf.LayoutDuration
31+ , recalcStyleSeconds: results[i].perf.RecalcStyleDuration
32+ , scriptDurationSeconds: results[i].perf.ScriptDuration
33+ }
34+ if (results[i].group in regrouped) {
35+ regrouped[results[i].group].results.push(reorged)
36+ } else {
37+ regrouped[results[i].group] = { results: [reorged], name: results[i].group }
38+ }
39+ }
40+ }
41+ return Object.values(regrouped)
42+}
43+
44+
45+async function write_results(allResults) {
46+ var resultsDir = "results"
47+ if (!fs.existsSync(resultsDir)) {
48+ fs.mkdirSync(resultsDir);
49+ }
50+ var results = JSON.stringify(allResults)
51+ var template = fs.readFileSync("./runtime/template/viewResults.html")
52+ // we embed the compiled js to avoid having to start a server to read the app.
53+ await compileToString(["./src/View/Results.elm"], { optimize: true }).then(function (compiled_elm_code) {
54+ const compiled = eval(`\`${template}\``)
55+ fs.writeFileSync(`./${resultsDir}/1.1.1-cand.html`, compiled)
56+ });
57+
58+ // fs.writeFileSync(`./${resultsDir}/1.1.1-candidate-optimized.json`, results)
59+}
60+
18(async () => {
61(async () => {
19 const browser = await puppeteer.launch();
62 const browser = await puppeteer.launch();
20− var instances = [{ module: "Baseline", value: "bench" }
63+ var instances = [{ module: "Baseline", group: null, value: "bench" }
21− , { module: "ManyElements", value: "elmUI1024" }
64+ , { module: "ManyElements", group: "elmUI", count: 1024, value: "elmUI1024" }
22− , { module: "ManyElements", value: "elmUI128" }
65+ , { module: "ManyElements", group: "elmUI", count: 128, value: "elmUI128" }
23− , { module: "ManyElements", value: "elmUI2048" }
66+ , { module: "ManyElements", group: "elmUI", count: 2048, value: "elmUI2048" }
24− , { module: "ManyElements", value: "elmUI24" }
67+ , { module: "ManyElements", group: "elmUI", count: 24, value: "elmUI24" }
25− , { module: "ManyElements", value: "elmUI256" }
68+ , { module: "ManyElements", group: "elmUI", count: 256, value: "elmUI256" }
26− , { module: "ManyElements", value: "elmUI4096" }
69+ , { module: "ManyElements", group: "elmUI", count: 4096, value: "elmUI4096" }
27− , { module: "ManyElements", value: "elmUI512" }
70+ , { module: "ManyElements", group: "elmUI", count: 512, value: "elmUI512" }
28− , { module: "ManyElements", value: "elmUI64" }
71+ , { module: "ManyElements", group: "elmUI", count: 64, value: "elmUI64" }
29− , { module: "ManyElements", value: "elmUI8192" }
72+ , { module: "ManyElements", group: "elmUI", count: 8192, value: "elmUI8192" }
30− , { module: "ManyElements", value: "viewHtml1024" }
73+ , { module: "ManyElements", group: "viewHtml", count: 1024, value: "viewHtml1024" }
31− , { module: "ManyElements", value: "viewHtml128" }
74+ , { module: "ManyElements", group: "viewHtml", count: 128, value: "viewHtml128" }
32− , { module: "ManyElements", value: "viewHtml2048" }
75+ , { module: "ManyElements", group: "viewHtml", count: 2048, value: "viewHtml2048" }
33− , { module: "ManyElements", value: "viewHtml24" }
76+ , { module: "ManyElements", group: "viewHtml", count: 24, value: "viewHtml24" }
34− , { module: "ManyElements", value: "viewHtml256" }
77+ , { module: "ManyElements", group: "viewHtml", count: 256, value: "viewHtml256" }
35− , { module: "ManyElements", value: "viewHtml4096" }
78+ , { module: "ManyElements", group: "viewHtml", count: 4096, value: "viewHtml4096" }
36− , { module: "ManyElements", value: "viewHtml512" }
79+ , { module: "ManyElements", group: "viewHtml", count: 512, value: "viewHtml512" }
37− , { module: "ManyElements", value: "viewHtml64" }
80+ , { module: "ManyElements", group: "viewHtml", count: 64, value: "viewHtml64" }
38− , { module: "ManyElements", value: "viewHtml8192" }
81+ , { module: "ManyElements", group: "viewHtml", count: 8192, value: "viewHtml8192" }
39− , { module: "ManyElements", value: "viewInline1024" }
82+ , { module: "ManyElements", group: "viewInline", count: 1024, value: "viewInline1024" }
40− , { module: "ManyElements", value: "viewInline128" }
83+ , { module: "ManyElements", group: "viewInline", count: 128, value: "viewInline128" }
41− , { module: "ManyElements", value: "viewInline2048" }
84+ , { module: "ManyElements", group: "viewInline", count: 2048, value: "viewInline2048" }
42− , { module: "ManyElements", value: "viewInline24" }
85+ , { module: "ManyElements", group: "viewInline", count: 24, value: "viewInline24" }
43− , { module: "ManyElements", value: "viewInline256" }
86+ , { module: "ManyElements", group: "viewInline", count: 256, value: "viewInline256" }
44− , { module: "ManyElements", value: "viewInline4096" }
87+ , { module: "ManyElements", group: "viewInline", count: 4096, value: "viewInline4096" }
45− , { module: "ManyElements", value: "viewInline512" }
88+ , { module: "ManyElements", group: "viewInline", count: 512, value: "viewInline512" }
46− , { module: "ManyElements", value: "viewInline64" }
89+ , { module: "ManyElements", group: "viewInline", count: 64, value: "viewInline64" }
47− , { module: "ManyElements", value: "viewInline8192" }
90+ , { module: "ManyElements", group: "viewInline", count: 8192, value: "viewInline8192" }
48
91
49 ]
92 ]
50
93
@@ -68,15 +111,17 @@ main = Benchmark.Render.toProgram ${item.module}.${item.value}
68
111
69 const page = await browser.newPage();
112 const page = await browser.newPage();
70 const results = await benchPage(page);
113 const results = await benchPage(page);
114+ results.group = item.group
115+ results.count = item.count
71 await page.close()
116 await page.close()
72 console.log(results)
117 console.log(results)
73 allResults.push(results)
118 allResults.push(results)
74 }
119 }
75 await browser.close();
120 await browser.close();
76
121
77− var resultsDir = "results"
122+
78− if (!fs.existsSync(resultsDir)) {
123+ allResults = regoupResults(allResults)
79− fs.mkdirSync(resultsDir);
124+
80− }
125+ write_results(allResults)
81− fs.writeFileSync(`./${resultsDir}/1.1.1-candidate-optimized.json`, JSON.stringify(allResults))
126+
82})();
127})();
benchmarks/runtime/template/viewResults.htmlnew file+24
@@ -0,0 +1,24 @@
1+<!DOCTYPE HTML>
2+<html>
3+<head>
4+ <meta charset="UTF-8">
5+ <title>Results</title>
6+ <script>
7+ ${compiled_elm_code}
8+ </script>
9+</head>
10+
11+<body>
12+ <div id="elm"></div>
13+ <script>
14+ const results = ${results}
15+
16+ var app = Elm.View.Results.init({
17+ node: document.getElementById('elm')
18+ });
19+
20+ app.ports.worldToElm.send(results)
21+
22+ </script>
23+</body>
24+</html>
benchmarks/src/ManyElements.elm+29−5
@@ -34,6 +34,7 @@ import Benchmark.Render
34import Element exposing (..)
34import Element exposing (..)
35import Element.Background as Background
35import Element.Background as Background
36import Element.Font as Font
36import Element.Font as Font
37+import Element.Keyed
37import Html
38import Html
38import Html.Attributes
39import Html.Attributes
39
40
@@ -235,13 +236,14 @@ elmUI name count =
235
236
236view model =
237view model =
237 Element.layout []
238 Element.layout []
238− (Element.column [ spacing 8, centerX ]
239+ (Element.Keyed.column [ spacing 8, centerX ]
239 (List.map (viewEl model.index) model.elements)
240 (List.map (viewEl model.index) model.elements)
240 )
241 )
241
242
242
243
243viewEl selectedIndex index =
244viewEl selectedIndex index =
245+ ( String.fromInt index
244− el
246+ , el
245 [ Background.color
247 [ Background.color
246 (if selectedIndex == index then
248 (if selectedIndex == index then
247 pink
249 pink
@@ -257,8 +259,16 @@ viewEl selectedIndex index =
257 white
259 white
258 )
260 )
259 , padding 24
261 , padding 24
262+ , width (px 500)
263+ , height (px 70)
260 ]
264 ]
265+ (if selectedIndex == index then
266+ text "selected"
267+
268+ else
261− (text "Hello!")
269+ text "Hello!"
270+ )
271+ )
262
272
263
273
264white =
274white =
@@ -315,7 +325,14 @@ viewHtmlElement selectedIndex index =
315 "pink"
325 "pink"
316 )
326 )
317 ]
327 ]
328+ [ Html.div []
329+ [ if selectedIndex == index then
330+ Html.text "selected"
331+
332+ else
318− [ Html.text "Hello!" ]
333+ Html.text "Hello!"
334+ ]
335+ ]
319
336
320
337
321{-| -}
338{-| -}
@@ -368,4 +385,11 @@ viewInlineHtmlElement selectedIndex index =
368 , Html.Attributes.style "padding" "24"
385 , Html.Attributes.style "padding" "24"
369 ]
386 ]
370 )
387 )
388+ [ Html.div []
389+ [ if selectedIndex == index then
390+ Html.text "selected"
391+
392+ else
371− [ Html.text "Hello!" ]
393+ Html.text "Hello!"
394+ ]
395+ ]
benchmarks/src/View/Results.elmnew file+249
@@ -0,0 +1,249 @@
1+port module View.Results exposing (main)
2+
3+import Browser
4+import Color
5+import Html
6+import Html.Attributes exposing (class)
7+import LineChart as LineChart
8+import LineChart.Area as Area
9+import LineChart.Axis as Axis
10+import LineChart.Axis.Intersection as Intersection
11+import LineChart.Colors as Colors
12+import LineChart.Container as Container
13+import LineChart.Dots as Dots
14+import LineChart.Events as Events
15+import LineChart.Grid as Grid
16+import LineChart.Interpolation as Interpolation
17+import LineChart.Junk as Junk exposing (..)
18+import LineChart.Legends as Legends
19+import LineChart.Line as Line
20+
21+
22+main =
23+ Browser.document
24+ { init =
25+ \() ->
26+ ( { data = [] }
27+ , Cmd.none
28+ )
29+ , view =
30+ \model ->
31+ { title = "Results"
32+ , body =
33+ [ Html.div
34+ [ class "container" ]
35+ ([ viewNodes model.data
36+ , viewFps model.data
37+ , viewTimeToPaint model.data
38+ ]
39+ ++ List.map viewTimeDistribution model.data
40+ )
41+ ]
42+ }
43+ , update =
44+ update
45+ , subscriptions =
46+ \model ->
47+ Sub.batch
48+ [ worldToElm Received
49+ ]
50+ }
51+
52+
53+type Msg
54+ = Received (List Data)
55+
56+
57+update : Msg -> Model -> ( Model, Cmd Msg )
58+update msg model =
59+ case msg of
60+ Received data ->
61+ ( { data = data }, Cmd.none )
62+
63+
64+
65+-- DATA
66+
67+
68+port worldToElm : (List Data -> msg) -> Sub msg
69+
70+
71+type alias Model =
72+ { data : List Data }
73+
74+
75+type alias Data =
76+ { name : String
77+ , results : List Render
78+ }
79+
80+
81+type alias Render =
82+ { count : Float
83+ , fps : Float
84+ , timeToFirstPaintMS : Float
85+ , nodes : Float
86+ , layoutSeconds : Float
87+ , recalcStyleSeconds : Float
88+ , scriptDurationSeconds : Float
89+ }
90+
91+
92+
93+-- Different Views
94+
95+
96+getColor i =
97+ case i of
98+ 0 ->
99+ Colors.green
100+
101+ 1 ->
102+ Colors.blue
103+
104+ 2 ->
105+ Colors.pink
106+
107+ _ ->
108+ Colors.gold
109+
110+
111+getShape i =
112+ Dots.triangle
113+
114+
115+viewFps : List Data -> Html.Html msg
116+viewFps datums =
117+ LineChart.viewCustom
118+ { y = Axis.default 800 "FPS" .fps
119+ , x = Axis.default 1400 "Base Count" .count
120+ , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
121+ , interpolation =
122+ -- Try out these different configs!
123+ -- Interpolation.linear
124+ Interpolation.monotone
125+
126+ -- Interpolation.stepped
127+ , intersection = Intersection.default
128+ , legends = Legends.default
129+ , events = Events.default
130+ , junk = Junk.default
131+ , grid = Grid.default
132+ , area = Area.default
133+ , line = Line.default
134+ , dots = Dots.default
135+ }
136+ (List.indexedMap viewResult datums)
137+
138+
139+viewResult i data =
140+ LineChart.line (getColor i) (getShape i) data.name (List.sortBy .count data.results)
141+
142+
143+viewTimeToPaint : List Data -> Html.Html msg
144+viewTimeToPaint datums =
145+ LineChart.viewCustom
146+ { y = Axis.default 800 "First Paint" (max 0 << .timeToFirstPaintMS)
147+ , x = Axis.default 1400 "Base Count" .count
148+ , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
149+ , interpolation =
150+ -- Try out these different configs!
151+ -- Interpolation.linear
152+ Interpolation.monotone
153+
154+ -- Interpolation.stepped
155+ , intersection = Intersection.default
156+ , legends = Legends.default
157+ , events = Events.default
158+ , junk = Junk.default
159+ , grid = Grid.default
160+ , area = Area.default
161+ , line = Line.default
162+ , dots = Dots.default
163+ }
164+ (List.indexedMap viewResult datums)
165+
166+
167+viewNodes : List Data -> Html.Html msg
168+viewNodes datums =
169+ LineChart.viewCustom
170+ { y = Axis.default 800 "Nodes" .nodes
171+ , x = Axis.default 1400 "Base Count" .count
172+ , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
173+ , interpolation =
174+ -- Try out these different configs!
175+ -- Interpolation.linear
176+ Interpolation.monotone
177+
178+ -- Interpolation.stepped
179+ , intersection = Intersection.default
180+ , legends = Legends.default
181+ , events = Events.default
182+ , junk = Junk.default
183+ , grid = Grid.default
184+ , area = Area.default
185+ , line = Line.default
186+ , dots = Dots.default
187+ }
188+ (List.indexedMap viewResult datums)
189+
190+
191+viewTimeDistribution : Data -> Html.Html msg
192+viewTimeDistribution data =
193+ let
194+ sorted =
195+ List.sortBy .count data.results
196+ in
197+ LineChart.viewCustom
198+ { y = Axis.default 800 (data.name ++ "(s)") .seconds
199+ , x = Axis.default 1400 "Base Count" .count
200+ , container = Container.styled "line-chart-1" [ ( "font-family", "monospace" ) ]
201+ , interpolation =
202+ -- Try out these different configs!
203+ -- Interpolation.linear
204+ Interpolation.monotone
205+
206+ -- Interpolation.stepped
207+ , intersection = Intersection.default
208+ , legends = Legends.default
209+ , events = Events.default
210+ , junk = Junk.default
211+ , grid = Grid.default
212+ , area = Area.stacked 0.5
213+ , line = Line.default
214+ , dots = Dots.default
215+ }
216+ [ LineChart.line (getColor 0)
217+ (getShape 0)
218+ "Layout"
219+ (List.map
220+ (\x ->
221+ { seconds = x.layoutSeconds
222+ , count = x.count
223+ }
224+ )
225+ sorted
226+ )
227+ , LineChart.line (getColor 1)
228+ (getShape 1)
229+ "RecalcStyle"
230+ (List.map
231+ (\x ->
232+ { seconds = x.recalcStyleSeconds
233+ , count = x.count
234+ }
235+ )
236+ sorted
237+ )
238+ , LineChart.line (getColor 2)
239+ (getShape 2)
240+ "Script"
241+ (List.map
242+ (\x ->
243+ { seconds = x.scriptDurationSeconds
244+ , count = x.count
245+ }
246+ )
247+ sorted
248+ )
249+ ]