Skip to content

perf(zero-solid): use reconcile#3682

Merged
grgbkr merged 6 commits intomainfrom
grgbkr/rollback-solid-perf
Feb 1, 2025
Merged

perf(zero-solid): use reconcile#3682
grgbkr merged 6 commits intomainfrom
grgbkr/rollback-solid-perf

Conversation

@grgbkr
Copy link
Contributor

@grgbkr grgbkr commented Feb 1, 2025

Setting solid store state many times is very slow, even if it is within a Solid batch call. In the hello-zero-solid app, if there are 3000 rows, processing the pushes during initial load takes 30 seconds. Nearly all of the time is in an internal solid function called unwrap.
image
image

The previous approach to solving this performance issue, #3608, is incorrect cause we cannot defer processing pushes, pushes have to be processed as received, because the relationship streams will be incorrect if processing is deferred. This was surfaced by issue https://bugs.rocicorp.dev/issue/3488
For comparison here is the performance of that approach:
image

This new approach maintains a copy of the view as a plain JS object which is updated on each push as the push is received, and then uses solid's reconcile to update the store at the end of the transaction.
image
The downside is we are ~2xing memory (actually this depends on how much of the data is interned string, its probably actually much less than 2x), and we are diffing the views (which doesnt seem to match the solid way of precise reactivity, we are translating a stream of precise updates into a big diff).

The upside is its

  1. correct
  2. event faster than the previous optimization 120 ms vs 970 ms. Probably because in this test case the diff is trivial.

ix(zero-solid): Only call setState once per transaction (#3608)""

This reverts commit 3171382.
@vercel
Copy link

vercel bot commented Feb 1, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
replicache-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 1, 2025 10:58pm
zbugs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 1, 2025 10:58pm

@github-actions
Copy link

github-actions bot commented Feb 1, 2025

🐰 Bencher Report

Branchgrgbkr/rollback-solid-perf
Testbedlocalhost
Click to view all benchmark results
BenchmarkFile SizeBenchmark Result
kilobytes (KB)
(Result Δ%)
Upper Boundary
kilobytes (KB)
(Limit %)
zero-package.tgz📈 view plot
🚷 view threshold
919.20
(+0.00%)
937.57
(98.04%)
zero.js📈 view plot
🚷 view threshold
171.95
(0.00%)
175.39
(98.04%)
zero.js.br📈 view plot
🚷 view threshold
47.96
(0.00%)
48.92
(98.04%)
🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

github-actions bot commented Feb 1, 2025

🐰 Bencher Report

Branchgrgbkr/rollback-solid-perf
Testbedlocalhost
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)📈 view plot
🚷 view threshold
76.05
(+4.96%)
67.96
(89.36%)
src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)📈 view plot
🚷 view threshold
95.78
(+1.11%)
90.32
(94.30%)
🐰 View full continuous benchmarking report in Bencher

@grgbkr grgbkr disabled auto-merge February 1, 2025 23:35
@grgbkr grgbkr merged commit 31072d3 into main Feb 1, 2025
13 checks passed
@grgbkr grgbkr deleted the grgbkr/rollback-solid-perf branch February 1, 2025 23:47
@arv
Copy link
Contributor

arv commented Feb 3, 2025

I have a branch that treats the data as immutable and does the bare updates. My initial goal was to use that for react because in react we currently deep copy (since the react external store needs a new object as a signal that something changed)..

If we are using reconcile we should use that.

I'll upload my branch for your looking...

Copy link
Contributor

@arv arv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but I'm afraid this might have other problems.

push(change: Change): void {
// Delay setting the state until the transaction commit.
this.#pendingChanges.push(change);
applyChange(this.#root, change, this.#input.getSchema(), '', this.#format);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? We are now changing the root without triggering updates. I'm concerned that there might be some way to see this intermediary state?

Maybe we should have a #newRoot and swap the root to that using reconcile in commit?

@arv
Copy link
Contributor

arv commented Feb 3, 2025

#3688

grgbkr added a commit that referenced this pull request Feb 4, 2025
…nd optimizing initial view build (#3701)

The `reconcile` optimization approach (#3682) was incorrect (I misunderstood how reconcile works), as was revealed by the tests added here: #3692.  

This approach goes back to using `produce`, but addresses the correctness issue by using an idea @tantaman came up with of reading the relationships of the changes at the time the push is received, but still queueing them to be applied to the solid store state on transaction completion.   We are careful to only read relationships in the changes actually used by `applyChanges`.

This solved the correctness problem, but while much faster than the 30 seconds prior to us trying to optimize this, the 3000 row initial load case in hello-zero-solid was still spending ~742ms in  SolidView#applyChanges.  
<img width="1434" alt="Pasted Graphic 2" src="https://github.com/user-attachments/assets/1179b9c4-90eb-4e3d-b0c7-e027ec51fb34" />


This is because the proxy used by solid `produce` is fairly slow.   To address this, I added an optimization for the case where the store's view is currently empty.  If the store is currently empty build up the view on a new plain old JS object root, which is returned for the new state.  This avoids building up large views from scratch using solid `produce`.  This brings the time for the 3000 row initial load case in hello-zero-solid to ~133ms.
<img width="1441" alt="Pasted Graphic 1" src="https://github.com/user-attachments/assets/0297b057-c826-420c-bdab-66ff2ee1f06d" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants