barneycarroll / emjay

Write Pug templates and pass them to Mithril
MIT License
1 stars 0 forks source link

Caching strategy #8

Closed barneycarroll closed 3 months ago

barneycarroll commented 4 months ago

Template computations ought to be cached: any given literal invocation with the same non-interpolated strings can be uniquely identified pre-computation, and as such some of that computation (interpolation parsing etc) ought to be short-circuited on subsequent computations.

Caching strategies are therefore desirable to avoid redundant computation and optimise performance.

From first principles, it would be desirable to simply cache the virtual DOM structures produced by templates. However, this conflicts with Mithrils virtual DOM diff strategy, which short-circuits the diff algorithm in the event of an incoming virtual node having reference equality to the previous node in place.

This presents a roadblock to straightforward caching of the current transform strategy: we need to produce new virtual nodes during each pass of the render loop in order for interpolated values to be persisted.

Split transformation process

Currently:

  1. Template function receives a TemplateStringArray & interpolations
  2. We map interpolations to placeholder strings identifying them by index
  3. We inject the placeholder strings into the TemplateStringArray to produce a plain string
  4. The string is passed to the Pug lexer to produce a list of Pug tokens
  5. The tokens are passed to the Pug parser to produce a Pug AST
  6. We iterate the Pug AST:
    1. Create a Mithril vdom node
    2. Identifying placeholders
    3. Retrieving corresponding interpolations
    4. And transform these as appropriate

Complexity is back-loaded, which minimises indirection and interstitial complexity but makes cache optimisation difficult.

Optimisation strategies:

Qualify placeholder substitutions after and before lexing

Qualifying the types of individual interpolation semantics before we produce Mithril vdom would allow us to strategise for different types of interpolation and optimise the process for caching:

  1. In the ‘after’ step of Pug parsing, any interpolations should add an identifying placeholder attribute to the containing element recursively: this will allow us to identify lower boundaries in branches of the tree which contain no interpolation at all, and can thus return cached Mithril vnodes which never need to be recomputed.
  2. Distinguishing between vnode & attribute interpolations and introducing extra placeholders in the case of the latter allows further optimisation by skipping substitution logic inference.
  3. The strategy behind step 1 could be extended further in order to identify the upper boundaries of sub-branches with interpolations: this could allow us to skip virtual DOM diffing for wrapping layers of static containers and explicitly recompute only the descendant sub-branches with interpolations.
barneycarroll commented 4 months ago

Current strategy is to directly cache the final state virtual DOM of static templates for direct reuse, and cache the intermediary DOM (before iterating with cloning procedures and interpolation logic) for interpolated templates.

This is plenty good enough in principle but is currently failing to persist updates.

barneycarroll commented 3 months ago

This is plenty good enough in principle but is currently failing to persist updates.

Fixed.

Version 1.0.1 exposes library internal functions as methods on the default export so caching strategy can be tested for. The nest step is to run performance tests compared to normal Mithril using https://github.com/pygy/bunchmark.js.

Since the initial speculations have been accomplished and there are no bugs with extant strategy, new issues can discuss further asks and tasks.