fable-arch icon indicating copy to clipboard operation
fable-arch copied to clipboard

Performance optimization

Open mastoj opened this issue 7 years ago • 17 comments

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.

mastoj avatar Sep 20 '16 21:09 mastoj

Maybe have a look at this: https://github.com/alfonsogarciacaro/FunScript.HTML/blob/master/src/samples/VanillaJS.fs#L68-L96

mastoj avatar Sep 22 '16 20:09 mastoj

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.

MangelMaxime avatar Dec 02 '16 21:12 MangelMaxime

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 avatar Jan 25 '17 05:01 MangelMaxime

@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 avatar Jan 31 '17 11:01 alfonsogarciacaro

@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.

MangelMaxime avatar Jan 31 '17 12:01 MangelMaxime

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 .

mastoj avatar Jan 31 '17 19:01 mastoj

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 avatar Feb 09 '17 19:02 mastoj

@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 avatar Feb 09 '17 20:02 alfonsogarciacaro

@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 :)

mastoj avatar Feb 09 '17 20:02 mastoj

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)

image

Non-Keyed (this is where fable-arch is)

image

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.

jmmk avatar Feb 20 '17 16:02 jmmk

Great stuff @jmmk. Also great that it seems like we came to the same conclusion about mapping direct to virtualdom elements.

mastoj avatar Feb 20 '17 18:02 mastoj

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 avatar Feb 23 '17 22:02 MangelMaxime

@mangelmaxime, I'm not 100 % sure about it but it looks that way.

mastoj avatar Feb 24 '17 06:02 mastoj

@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 avatar Feb 24 '17 08:02 alfonsogarciacaro

@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?

mastoj avatar Feb 24 '17 08:02 mastoj

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.

MangelMaxime avatar Feb 24 '17 16:02 MangelMaxime

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.

alfonsogarciacaro avatar Feb 24 '17 16:02 alfonsogarciacaro