babylonhealth/Bento

Enable Size Caching by default

andersio opened this issue · 1 comments

Problem Statement

Enable size caching by default in Bento, for both UITableView and UICollectionView (if using UICollectionViewFlowLayout).

This helps scrolling performance for use cases with a low frequency of changes & a moderate amount of screen complexity.

The problem is, however, the incurred cost during re-rendering of new Boxs. Bento must invalidate all cached sizes before starting to render any new Box, unless the component does not change at a given ID path. Pragmatically speaking, this cannot happen for a majority of components (e.g. BentoKit) falling back to the pointer equality (conservative but correct), due to them carrying non-equatable properties e.g. callback.

So if we enable size caching without addressing this issue, for screens that have high burst of state changes e.g. reacting to continuous text input, the cost of precomputation would be incurred every time a new Box is rendered, which is incredibly unideal.

Synthetic Benchmark

TitledDescription, 1000 iterations, -O -wholemodule
Source

Item Result (1000 iterations)
Full equality of layout affecting properties* Small Strings: 
sub 1 ms.
5000+ character strings: sub 2 ms

Custom Height Computation 
sub 100 ms

AutoLayout height computation
 > 1000 ms

* Style sheets & RAS properties are compared by value.

Observation

Evaluating equality of a subset of component properties is considerably cheaper (2-3 order of magnitude) than computing the actual layout height.

In order words, it is beneficial and pragmatic to evaluate equality so as to avoid as much layout computation as possible, lowering the performance impact of sizing ahead of time.

Proposed optimization

  • Introduce a notion of “component layout equivalence” to replace component equation.





    When we say C.isLayoutEquivalent(a, b) == true, all properties contributing to the layout from both instances are equal. In other words, rendering a and b should result in the same exact layout.

    Renderable would however provide a false returning default implementation. This effectively replicates the current behaviour (still correct but more expensive), while being (mostly) source compatible.

    BentoKit components should provide implementations for this requirement.

  • Remove Equatable inheritance from Renderable.

    We should not reappropriate == for component layout equivalence, since it would violate the substitutability requirement mandated by the Equatable contract.

  • With the above two measures in place, Bento may start pre-compute component height using AutoLayout by default
.

    Bento should invalidate only cached size for ID paths failing the component layout equivalence test.

  • (Optionally) Replacing HeightCustomizing with something more general.

Implementation Plan:

  • Introduce AdapterStore which is capable of storing cached size and related attributes, and perform invalidation as required. TableViewAdapterBase would integrate AdapterStore, but the size caching capability would not be enabled. (Done, #141)

  • Introduce SizeCachingTableView which would have size caching capability enabled. (Done, #141)

  • Upgrade BoxViewController to use SizeCachingTableView. (Done, #145)

  • Support pre-sizing layout pass that would enable layouts involving multi-line text to work reliably. (Done, #121)

  • Remove HeightCustomizing.

  • Introduce SizeCachingCollectionView. (Need reevaluation)

  • Upgrade screens using a custom size cache implementation to use SizeCachingCollectionView. (Need reevaluation)

  • Introduce Layout Equivalence.