DEV Community

Cover image for Super Charging Fine-Grained Reactive Performance

Super Charging Fine-Grained Reactive Performance

Milo on December 01, 2022

What's a Reactive Library? Reactivity is the future of JS frameworks! Reactivity allows you to write lazy variables that are efficiently...
Collapse
 
ryansolid profile image
Ryan Carniato • Edited

Awesome work.

I like that you looked at the difference between width and depth. A lot of reactive benchmarks focus on depth, but most reactive UIs are actually wider than than they are deep from a reactive perspective. Like how far do you derive a value, 2 or 3 levels tops, where these benchmarks tend to look in the 1000s. But having the same value read in 10s or maybe 100s of places in the UI is more common.

I look forward to how we can make a system like this work with concurrent rendering.

Collapse
 
mfp22 profile image
Mike Pearson

I'd like to see how Sloid does on these

Collapse
 
mindplay profile image
Rasmus Schultz

Benchmark against @maverick-js/observables please. It also claims to be the fastest.

github.com/maverick-js/observables...

Collapse
 
ryansolid profile image
Ryan Carniato

I don't think they are claiming that anymore. Looking at their benchmarks Preact Signals are faster than them in those depth based tests.

Also Jin added Reactively to his benchmarks too and it topped it:

Definitely should test it, but Reactively seems to holding up well.

Collapse
 
mindplay profile image
Rasmus Schultz

It's probably not very important anyway - any of these libraries are more than fast enough. Unless you were building a highly real-time application (like a game) the difference likely wouldn't be measurable in real-world applications.

But since clearly it's a contest on metrics, claims like that should be backed up - for those people who are picking a library based on its performance. Most of us probably should pick based on merit rather than metrics. πŸ˜‹

Collapse
 
thetarnav profile image
Damian Tarnawski

From what I remember, the current solid algorithm also does coloring, to figure out priorities but then it updates all un-updated memos anyway for predictability. Is this somewhat correct?

Collapse
 
modderme123 profile image
Milo

That's correct. The current Solid algorithm pushes all updates into a queue and then gets each item in the queue using the same coloring algorithm as Reactively

Collapse
 
ryansolid profile image
Ryan Carniato

Solid today over queues, but it doesn't run any unnecessary computations. Basically it iterates to check the state of nodes more than it needs to as it 1. faster than sorting, 2. easier to interrupt for time slicing.

Collapse
 
devinrhode2 profile image
Devin Rhode

Let's translate it to rust and then create a DB out of it.

Collapse
 
davedbase profile image
David Di Biase

A Rust implementation would be interesting!

Collapse
 
mrjjwright profile image
John Wright

Bad.ass

Collapse
 
johnnyjxliaw121 profile image
Johnny Liaw

Hey, thanks so much for the article! I found it extremely informative.

Curious how you found out about the internal implementation of MobX? I'm currently diving into the codebase but I'm having trouble seeing how in code how it tackles the diamond problem... I'm most likely not looking in the right area

Collapse
 
modderme123 profile image
Milo

Thanks for the kind words! It looks like since MobX 2.5, MobX has switched to a model with lazy computeds, and currently uses a similar algorithm to Reactively & Solid. Because of this, computeds are pulled on demand instead of evaluated eagerly so to avoid evaluating A in the diamond twice, it just needs to mark it as clean/up to date after the first evaluation. The original version avoids overevaluating any element in the diamond by predicting how many times it would be reevaluated (how many parents would update), and then only updating once that many parents have already been updated.

Here's the source of where MobX used to mark dependencies as stale: github.com/mobxjs/mobx/blob/2.4.4/...

You can compare that with the modern implementation using STALE (red), POSSIBLY_STALE (green), and UP_TO_DATE (gray): github.com/mobxjs/mobx/blob/v6.0.2...

Collapse
 
artalar profile image
Artyom

Hi! The bench approach is very interesting, thank you for sharing this!
But I can not reproduce example from the readme, your package just have no "reactively" variable in the sources, looks like this is the bundling issue?
Also, the important question IMHO, is Reactively support atomicity guarantees for state consistency or could it lead to unpredictable behaviour if error occurs?

Collapse
 
modderme123 profile image
Milo

Ah yep, looks like what might be missing from the source is

export const reactive = (x) => new Reactive(x);
Enter fullscreen mode Exit fullscreen mode

As for atomicity:
One of the really neat benefits of lazy state is that you don't need batching or atomicity within a transaction for consistency. However, it is still necessary to be able to catch errors and rollback. Currently Reactively doesn't support rolling back, but it should be possible to add in a future version.

Collapse
 
artalar profile image
Artyom

I think atomicity should be default (as React.js fails all render which cause an error) . The problem with reactive programming is that error propagation is not obvious and could lead to really bad cases, here is a simple example

Collapse
 
firewatch profile image
jzymx50 • Edited

When will the second part of the benchmark be posted? Looking forward to comparing the data structures each framework uses!

Collapse
 
dmitryefimenko profile image
Dmitry A. Efimenko

How come RxJS is not included? The most obvious choice for reactivity IMHO

Collapse
 
modderme123 profile image
Milo

RxJS has a slightly different API and semantics that don't match the rest of the frameworks in the benchmark. For example, it has no concept of a computed & signal.

That being said, I would be open to a PR that adds RxJS to the benchmark (these are the files that you would need to modify github.com/modderme123/reactively/...). It should be possible to do that pretty quickly using something like github.com/kosich/rxjs-autorun.