Fusion icon indicating copy to clipboard operation
Fusion copied to clipboard

Lazy evaluation / hybrid push-pull execution model

Open dphfox opened this issue 1 year ago • 11 comments

Closes #144. Implements Reactively's hybrid push-pull algorithm for Fusion's graph objects, as described on Technical Fluff and as described by Reactively's author.

dphfox avatar Apr 15 '24 18:04 dphfox

Closes #303.

dphfox avatar Apr 15 '24 18:04 dphfox

Diverged a bit from the algorithm as written. Now, it only paints validity/invalidity, as well as storing a change time for avoiding computations for non-meaningful changes.

dphfox avatar Apr 16 '24 00:04 dphfox

New algorithm documented on Technical Fluff: https://fluff.blog/2024/04/16/monotonic-painting.html

dphfox avatar Apr 16 '24 20:04 dphfox

The new execution model exposed a shortcoming of the existing For internals, where there was an infinite loop between pair processors and the main object.

So I've been mulling over the problem for a few days, and I realised that I probably should have a better mental model of what should be going on inside a For object.

This is the structure I landed on:

image

  • TI doesn't produce a value itself (graph only), but instead manages the disassembly of the table into individual KI and VI state objects as efficiently as possible, reconciling them with the key/value pairs in the input table.
  • KI and VI are basically just state objects holding a value. They exist purely to filter updates from TI that wouldn't be meaningfully different.
    • Perhaps it'd be better for the user to construct these as required, given they're not always used.
  • KO and VO are constructed via a user callback and have arbitrary dependencies. Typically they'll depend on the key and value inputs they're given from KI and/or VI.
  • TO assembles all KO/VO outputs into key/value pairs in the output table. Its dependencies and state of validation are managed by TI.

dphfox avatar Apr 30 '24 14:04 dphfox

image

It's been so long.

dphfox avatar May 13 '24 20:05 dphfox

Tweens are now up and running, and alongside them, there is a bevy of new internal utilities for managing timing based objects. This will make some other things much easier to implement in the future!

The system is designed to work with custom time sources too, so it should be flexible.

dphfox avatar May 15 '24 01:05 dphfox

So I've been playing around with the bugs in this branch for a short while this evening. As best as I can tell, this branch messes up the lifetimes of something somehow - I suspect something that promises to create a new inner scope for a callback is instead returning an outer scope somewhere. Just a hunch.

dphfox avatar Jun 15 '24 23:06 dphfox

Fixed some of the major lifetime issues - after a lot of diagnosis, it turns out the issue was that New calls were being cached, which is incorrect because scope upvalues were being used in the cached functions.

dphfox avatar Jun 16 '24 16:06 dphfox

image

Ported the Word Game example just fine... so looks like I'll need a more complex example to figure out what's going wrong.

dphfox avatar Jun 17 '24 09:06 dphfox

if you create a tween with a repeat count of -1 then the first time its evaluated self._activeFrom is nil so it sets self._activeElapsed to self._activeDuration (which is infinity) which then causes getTweenRatio to return nan so it gives a warning

Will need to fix this

dphfox avatar Jun 21 '24 21:06 dphfox

Will also need to fix the infinite loop check in change to allow for cyclic changes if they happen through a dependency that doesn't meaningfully change (likely, this just means allowing cycles in change).

dphfox avatar Jun 21 '24 21:06 dphfox

Something I realise now about the way that execution should work; the oldest graph objects should be evaluated first.

Here's why; suppose that there's an Observer A which creates or destroys another Observer B watching the same state. This is a simple dynamic graph.

First observation: every time an evaluate runs, we no longer know what the graph is going to look like. The current code does not account for this, so scope == nil checks should be added.

Second observation: intuitively, if Observer A decides to destroy B, then B should not be given the chance to evaluate. This isn't guaranteed either because eager evaluation order is not specified by change. So Fusion should track creation time and sort by it before executing eager objects.

https://fluff.blog/2024/07/14/glitches-in-dynamic-reactive-graphs.html

dphfox avatar Jul 14 '24 20:07 dphfox

Alright. So I've been looking into the issues that are currently blocking this, and it looks like there's a problem with re-entrant reactive updates. Specifically; if an eagerly updated object causes another update to occur, it can prevent another object from receiving eager updates.

dphfox avatar Jul 28 '24 05:07 dphfox