This is an implementation of of the js-framework benchmark using shadow-grove.
Latest run using everything. With latest shadow.grove.kv
changes.
Components and KV still carry quite a bit of overhead when compared to this. Although it really doesn't matter, even the Full variant spends more time in the Browser rendering than actual JS.
No Components or DB. Just atom rendered from the root in pure interpreted Hiccup. No fragments. This is now actually competitive and no longer laughably slow because of a bug.
Just some updated numbers to see how things have developed in the meantime. svelte is cheating it seems, gotta look into what they are doing.
No Grove, Components or DB. Just atom rendered from the root. Closest featureset in comparison to everything else.
Just a quick check what the numbers look like after API cleanup. Did another Light run as well, looks decent. Main overhead still in EQL/DB indexing it seems.
Should stop playing these benchmark games. There are some interesting cases to consider and a lot of room for improvement, but also far more important things to take care of first.
Ran the benchmark against the current js-framework-benchmark
for the 0.2.0
shadow-grove release. Also ran against most common used JS frameworks according to the State of JS Survey. Numbers still look decent given that I haven't done any performance work since the last time I ran these.
Last few simple query optimizations. At this point I should really start focusing on something more useful. This is fast enough now.
Full Benchmark HTML reports:
- https://code.thheller.com/demos/js-framework-benchmark/benchmark-full.html
- https://code.thheller.com/demos/js-framework-benchmark/benchmark-light.html
shadow-cljs Build Reports:
- https://code.thheller.com/demos/js-framework-benchmark/full.html
- https://code.thheller.com/demos/js-framework-benchmark/light.html
Optimized render-seq
a little more since we can exploit identical?
checks to skip some work. Mostly affects light
variant since that has to render-seq
in select row
.
Saw another easy win in clear rows
and couldn't resist taking it. Seems to be at the point where now more time is spent in event data processing than any DOM related update work.
One more. Realized that all query indexing work can be delayed and moved it to async queue. Minor gain overall but I'll take it.
unchanged since it doesn't use EQL queries
Happy with performance for now. Added a couple more impls for comparison. Any CLJS impls I missed?
full
variant pays for faster update cycles by having slower mount/unmount since it has to hook up the EQL queries. There is lots of room left to make that faster overall.light
variant doesn't do EQL butselect row
is rather slow. Mostly of time is spent inrender-seq
. Likely that can be tweaked more but good enough for now. Easily escaped by skippingrender-seq
entirely and updating row directly as EQL variant does.
Things look better with optimized render-seq
. Now just need to get query indexing to a decent state and I'm happy with the performance.
Light variant still about same overall score. Trades faster create/clear for select row
being much slower. Didn't optimize the actual diffing that occurs there yet. Full doesn't need to diff because it knows which rows were modified, that's were the power of the normalized DB really shines.
To be honest those numbers already look much much better than anticipated. Almost suspiciously so. Need some kind of better real-world related benchmark but things seem to be at the point where it matters much more what the app does than the underlying core algos.
Added a light
variant that doesn't use any normalized DB or EQL queries. Faster in some aspects actually slower in others. Suffers greatly from render-seq
behavior being bad. full
variant actually surprisingly good.
- create (many) rows: expected to be slow because of setting up and running the EQL to get the data
- replace all rows: I guess the warmup makes all the difference here. no clue how it could be faster than create otherwise
- partial update: decent, slow because of EQL queries
- select row: fast since computation is done in event handler instead of dynamic EQL attribute
- swap rows: only has to update a vector via assoc (fast) and then swap two DOM nodes (also fast)
- remove row:
render-seq
behavior terrible for removals - append: expected this to be slower but I guess it had enough warmup time when created the first 10,000.
- clear: most time spent in clearing up the normalized DB and EQL query machinery. actual DOM time is a tiny fraction.
This version had terrible select row
performance because of a EQL computed attribute. Instead now calculating it once in the ::select!
event.