Closed TimLariviere closed 4 years ago
/azp run full build
for updateIncremental would be worth take a look at https://github.com/fsprojects/Fabulous/pull/780 to reduce unneccessary allocations, i think
@vshapenko For now, I'm currently looking at reducing allocations in Collections.diff
, as this is where most allocations come from.
Ok, I managed to reduce allocations in Collections.diff
with a dirty trick.
The problem was essentially the fact that Collections.diff
and Collections.adaptForObservableCollection
were returning an F# list with Operation
to apply to a list to go from State A to State B. Those functions are called a lot of times (~20.000 times just for adding 20 people to the contact list in FabulousContacts), so any allocation quickly become a lot of allocations...
So to avoid having those returned F# lists, I preallocated an array from the ArrayPool
for the maximum size the algorithm will need and made diff
/adaptForObservableCollection
directly work with it.
Also made sure that all local functions were not implicitly capturing variables to avoid allocations.
I will need to clean up this algorithm before merging.
By top allocations
By methods
By assemblies
Top allocations
Looks very promising. Looking forward to try this merge in real action
@vshapenko Yes! I will surely publish a preview release.
Fabulous has to box struct to store them in ViewElement because it stores any kind of value as obj
.
So, to reduce allocations on structs (Xamarin.Forms.Color
, Xamarin.Forms.Thickness
and Xamarin.Forms.LayoutOptions
), I put memoization mechanism by storing the boxed values and reusing those instead of reboxing each time.
Also I've been bit by https://github.com/dotnet/fsharp/issues/526 when trying to reduce struct boxing.
So to avoid implicit boxing of structs like ValueOption
, I replaced the =
operator with a custom function.
By top allocations
By methods
By assemblies
Top allocations
This time, I changed the way we update Attached Properties to avoid passing an overload dictionary each time we update a ViewElement.
Instead the overloaded updater is a function stored in ViewElement
just like update
.
This removed instantiations of the 5th and 7th most instantiated types, but with apparently little effect on the global memory allocation.
By top allocations
By methods
By assemblies
Top allocations
@TimLariviere , preview is ready to try?
It's almost ready. Just need to fix the few comments I made and then I'll merge this PR and push the preview package to NuGet. Should be done by the end of day.
I'll continue working on allocations in a next PR.
/azp run full build
/azp run full build
/azp run full build
/azp run full build
/azp run full build
It's a concurrent PR to #802 because I'm testing a different approach. Ultimately only one PR will be merged, containing the best of the 2.
I have been profiling FabulousContacts (slightly modified version) on Windows, in an Android emulator.
Here's the list of changes I made
ViewBuilders.Update...
when comparing old/new values withmatch ... with
Collections.Operation
printf
inViewUpdaters.updateNavigationPages
(should use Program.withConsoleTrace instead in a future version)Array.tryFind (fun x -> ...)
by a simple recursive function to avoid lambda context captureHere's the result as of today (left: Fabulous 0.57, right: this PR)
By allocations
By methods
By assemblies
Top allocations