fable-arch
fable-arch copied to clipboard
Performance optimization
As it is now the performance is sort of ok, but the implementation is 2-3 times slower than elm in https://github.com/mastoj/ui-perf, and that shouldn't be the case. I would expect the fable solution to be somewhat slower since it is more general purpose compared to elm.
- [ ] Check where the bottle necks are, in fable-arch.app, fable-arch.virtualdom or in fable core library.
- [ ] Fix an implementation that also supports
requestanimationframe
that is faster. This logic should probably move into the renderer and not in the app as it is now. The app sets the rendering rate not the renderer.
Maybe have a look at this: https://github.com/alfonsogarciacaro/FunScript.HTML/blob/master/src/samples/VanillaJS.fs#L68-L96
Just as a memo:
It looks like using createObj is one of the things that have an impact on performance, and that is done during rendering when creating attributes.
Just an idea to pin it in this topic.
We could try to use KeyValueList to pass the data to VirtualDom. KeyValueList output JSObject directly.
@MangelMaxime createObj
also outputs a literal JS object if you pass all the members at once:
createObj [ "foo" ==> 1; "bar" ==> 5]
{ foo: 1, bar: 5 }
I'm not sure if KeyValueList
would help here without a big refactoring because you'll probably have to build the options object dynamically at the end.
Has the performance be measured with the latest version? If I remember well, when @mastoj created the performance test the biggest bottleneck was recalculating the whole view tree for every change.
@alfonsogarciacaro
Thanks for the information, I am not sure neither.
But using the KeyValueList
could also add strongly typed view but this is another topic :)
I still need to finish the docs site before going for the performance or new renderer implementation.
As I said in a comment somewhere, I do think the main performance issue is that we do some extra translation between DOM representations compared to elm for example. The elm implementation of the dsl maps directly down to the virtualdom, while in fable-arch we first have the dsl and that is represented in js created by fable and then there is a translation of that js to virtualdom js. That translation is probably expensive since there are a lot of objects that are being translated.
With that said, I'm not a js performance expert and it might be more things that could help on performance.
On Tue, 31 Jan 2017 at 13:20 Maxime Mangel [email protected] wrote:
@alfonsogarciacaro https://github.com/alfonsogarciacaro Thanks for the information, I am not sure neither. But using the KeyValueList could also add strongly typed view but this is another topic :)
I still need to finish the docs site before going for the performance or new renderer implementation.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fable-compiler/fable-arch/issues/32#issuecomment-276349367, or mute the thread https://github.com/notifications/unsubscribe-auth/AAemsO0i9Z_hxcfp00c4FyN2BT_6m4KPks5rXycbgaJpZM4KCKRz .
One thing that might be an improvement would be to map the HTML dsl directly to virtualdom, so when you render you basically have the virtualdom already and don't need to translate the HTML dsl to virtualdom. The drawback, and why I didn't do that at the first place, is that then the dsl will be tightly coupled to virtualdom and harder to use on the server side.
@mastoj That may be a good idea. FYI, in the next Fable version we will probably go back at distributing sources (but using a special <FablePackage />
build item that will translate to a project reference) so it will be possible again to use inline functions across projects. This means you could have several DSLs with the same facade but different content and the only thing the user needs to change them is opening a different module.
@alfonsogarciacaro I was thinking if one could do something like you described above. Not sure I'll have time to do it at the moment though, I think it is a significant change. Just sharing my thoughts :)
There's a pretty good benchmark suite at https://github.com/krausest/js-framework-benchmark that compares a lot of implementations.
I added one for fable-arch-virtualdom
and fable-arch-react
in the fable-arch
branch here: https://github.com/jmmk/js-framework-benchmark/tree/fable-arch
Here are the results on my machine (I didn't run every benchmark. Some of them gave me errors so I just chose a few that worked):
Keyed (included to show elm results)
Non-Keyed (this is where fable-arch is)
You can see that react seems to be faster, but even the virtualdom implementation is usually within 3-4x of the faster frameworks.
I ran a few profiles in the Chrome debugging tools on the virtualdom page to see if anything jumped out at me. It seems like a lot of work is being done inside createTree
. The solution mentioned above (map straight to virtualdom elements) would probably help a lot here. It could also be that the collection implementations in fable-core are not yet optimized (a lot of time is spent in map
/fold
/etc.), but this could just be because of the above mentioned createObj
as I didn't dig in too far.
Great stuff @jmmk. Also great that it seems like we came to the same conclusion about mapping direct to virtualdom elements.
Ok, I am working on the performance issue.
I just want to confirm something with you guys. We are ok to say that the problem come from the mapping we are using with Fable-Arch.
By mapping I mean: Writing a custom DSL and then mapping this DSL over the VirtualDom implementation via hyperscript. (code responsible of this action)
If yes, I think I have a solution. I still need to dig trough but this will change a little the DSL for the new renderer. As it's a new renderer no breaking change are planned with existing code base.
The DSL I have in mind is a little more verbose but also could support intellisense and be extended by the user if needed.
@mangelmaxime, I'm not 100 % sure about it but it looks that way.
@MangelMaxime That suggestion looks very good and intellisense is very important :+1:
I haven't checked fable-arch code recently but when I did a few months ago I think one of the problem is the whole tree was recreated for every change, while in React, for example, only views that need updating are recreated. Something like view caching and comparing the new props (sorry, React terminology) with the old ones before running the view function may help.
@alfonsogarciacaro I think that is an extra optimization on top of the one @MangelMaxime is talking about. I think we first need to change so we don't convert from one dsl (arch html) to another (vdom), that should be mapped directly. After that we could probably add some memoziation or whatnot to make it event faster, but that might be a choice for each developer to make?
My current work is to map directly VDom so somehow Fable-Arch DSL will have the same behavior as Hyperscript. No extra layer or a minimal one for mapping the events with the handler for example.
And I agree with @mastoj saying that cache is an extra optimization coming after the DSL.
Another idea could be, taking advantage of the fact you can apply parches to the virtual-dom, is to update each componente independently. I'm not a huge fan of computing the whole tree for every minor change (even if virtual-dom automatically calculates the actual patches to the real DOM which should be the main bottleneck). I'm not sure yet how it could be done elegantly, but if the components could subscribe directly to state changes and render themselves, this could increase performance and also make the components reusable which is a missing feature in the Elm architecture.