In the process of modernizing Knockout for version 4.0 (now live at the monorepo tko), I've hit some performance issues.
Among other changes, some inner-loop things have been converted to ES6 classes, and it's causing some major performance problems. I'd rather not undo that effort as it adds quite a bit of clarity to some key code, so I'd like to solicit some input on how to improve the ES6 code.
I've set up some simple samples for profiling here:
Knockout Alpha2 - 349ms
Knockout Alpha3 (prerelease) - 622ms
The code in the jsFiddles is as follows, and it's prototypical of the slowdowns being experienced across a number of bindings.
HTML:
<div id='x'>
<strong>{{ count }} / {{ time }} ms</strong>
<custom-component></custom-component>
</div>
<div id='cc-template'>
cc
{{# unless: finished }}
<custom-component></custom-component>
{{ /unless }}
</div>
Javascript:
let count = ko.observable(0)
let time = ko.observable(false)
const start = performance.now()
const ITERATIONS = 1000
class viewModel {
constructor () {
this.finished = count() > ITERATIONS
count(count() + 1)
time(performance.now() - start)
}
}
ko.components.register("custom-component", {
viewModel, template: {element: 'cc-template'}
})
ko.applyBindings({count, time}, document.getElementById('x'))
If you compare the Javascript profiles, the call trees are nearly identical (notwithstanding e.g. the ES6 changes). It looks like the extra time in Alpha3 is at the leaf calls, making them harder to identify, so I speculate based on the profile comparisons that the problem is a few fold, including:
- some non-optimizing or de-optimizing happening
- some loops replaced with slower, native calls e.g.
Array.from
- more intermediate minor garbage collections
- less native optimization for ES6isms
In any case I haven't rooted out which, if any of these, is the problem yet – or where exactly they might be occurring.
I would be most grateful for insights and help identifying where we can make optimizations to get the performance back up to, or even above, par.
To consolidate any answers, I've started an answer as a wiki:
template
andif
/ifnot
/unless
/with
/else
andforeach
bindings — 3074AA9