Skip to content

perf: coalesce auto-save serialization while a write is in flight - #90

Open
imurashka wants to merge 4 commits into
mainfrom
ivan/coalesce-serialization
Open

perf: coalesce auto-save serialization while a write is in flight#90
imurashka wants to merge 4 commits into
mainfrom
ivan/coalesce-serialization

Conversation

@imurashka

Copy link
Copy Markdown
Contributor

Auto-save no longer serializes the whole storage on every change. While the previous snapshot is still on its way to the disk, a change only marks the storage as changed; the background writer asks the storage to serialize once more when it becomes free, through the SynchronizationContext captured when the storage was built.

A burst of 200 Set calls now costs one serialization instead of 200. Measured in the editor on Windows: 54 keys 2.05 ms -> 0.02 ms, 1 710 keys 49.22 ms -> 0.01 ms, 16 200 keys 457.88 ms -> 0.01 ms. Serialization still happens on the thread that owns the storage, so nothing new is read from another thread. A storage built without a SynchronizationContext keeps serializing on every change, exactly as before.

While the previous snapshot is still on its way to the disk, a change no
longer serializes the whole storage: it only marks the storage as changed.
The background writer asks the storage to serialize once more when it
becomes free, through the SynchronizationContext captured at build time.
…orage

Look up a registered section through a type-to-index dictionary instead of
scanning the section list twice with two different comparisons. Share the
type-mismatch decision between Set and SetRaw, and keep reactive collection
tracking in one pair of methods instead of five copies.

Hand the deferred-save callback to the writer at construction so it stops
travelling through a settable property on three types, and move the
test-only AddRange helpers out of the runtime assembly.
ReactiveList, ReactiveSet and ReactiveDictionary each carried their own copy
of the dispose flag, the OnChanged event, SetDirty and ThrowIfDisposed. Move
that contract into ReactiveCollection so a change to it lands in one place.

Build the nested storage key list in a single pass instead of a LINQ chain,
and reuse the prefix check that RemoveAll already needed.
MarkChanged, DecreaseCounter and SaveDeferredChanges each carried their own
copy of the "auto-save is on, no change scope is open, there is something to
save" condition. Route the other two through MarkChanged so the policy lives
in one method.

Give the nested storage key list its capacity up front and reuse a cached
prefix predicate, post the deferred-save callback through a static callback
instead of a closure, and share one storage-opening helper across the
persistence fixtures.
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.

1 participant