-
Notifications
You must be signed in to change notification settings - Fork 576
perf(tree): Lazily initialize event buffer #27382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+75
−12
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
080d7f7
tree: lazily allocate KernelEventBuffer on first events access
Josmithr 96efd43
test(tree): cover lazy KernelEventBuffer allocation semantics
Josmithr 3bc87ab
Merge branch 'main' of https://github.com/microsoft/FluidFramework in…
Josmithr 8e05fb3
test: Fix typo in test name
Josmithr 3514df0
docs: Update comment
Josmithr 6169858
improvement: More consistent assertions
Josmithr 6ed038d
Merge branch 'tree/lazy-event-buffer' of https://github.com/Josmithr/…
Josmithr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,14 +120,16 @@ export class TreeNodeKernel { | |
| #hydrationState: HydrationState; | ||
|
|
||
| /** | ||
| * Events registered before hydration. | ||
| * Handler for events listeners registered with the kernel. | ||
| * | ||
| * @remarks | ||
| * Since these are usually not used, they are allocated lazily as an optimization. | ||
| * The laziness also avoids extra forwarding overhead for events from this kernel's anchor node and also avoids registering for events that are unneeded. | ||
| * This means optimizations like skipping processing data in subtrees where no subtreeChanged events are subscribed to would be able to work, | ||
| * since the kernel does not unconditionally subscribe to those events (like a design which simply forwards all events would). | ||
| * Supports event buffering via {@link withBufferedEvents}. | ||
| * | ||
| * Allocated lazily on first access to {@link TreeNodeKernel.events}. | ||
| * We expect the majority of nodes to never have event listeners registered, so | ||
| * deferring construction avoids per-kernel allocations. | ||
| */ | ||
| readonly #eventBuffer: KernelEventBuffer; | ||
| #eventBuffer: KernelEventBuffer | undefined; | ||
|
|
||
| /** | ||
| * Create a TreeNodeKernel which can be looked up with {@link getKernel}. | ||
|
|
@@ -157,12 +159,9 @@ export class TreeNodeKernel { | |
| this.#hydrationState = { | ||
| innerNode, | ||
| }; | ||
|
|
||
| this.#eventBuffer = new KernelEventBuffer(innerNode.events); | ||
| } else { | ||
| // Hydrated case | ||
| this.#hydrationState = this.createHydratedState(innerNode); | ||
| this.#eventBuffer = new KernelEventBuffer(innerNode.anchorNode.events); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -190,8 +189,10 @@ export class TreeNodeKernel { | |
|
|
||
| this.#hydrationState = this.createHydratedState(inner); | ||
|
|
||
| // Lazily migrate existing event listeners to the anchor node | ||
| this.#eventBuffer.migrateEventSource(inner.anchorNode.events); | ||
| // Lazily migrate existing event listeners to the anchor node. | ||
| // If no one ever subscribed to this kernel's events, the buffer was never allocated | ||
| // and there is nothing to migrate. | ||
| this.#eventBuffer?.migrateEventSource(inner.anchorNode.events); | ||
| } | ||
|
|
||
| private createHydratedState(innerNode: HydratedFlexTreeNode): HydratedState { | ||
|
|
@@ -233,6 +234,14 @@ export class TreeNodeKernel { | |
| } | ||
|
|
||
| public get events(): Listenable<KernelEvents> { | ||
| assert(!this.disposed, "Cannot register events on a disposed node"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why doesn't this also use |
||
| // Allocate the buffer on first access. See {@link TreeNodeKernel.#eventBuffer} for rationale. | ||
| if (this.#eventBuffer === undefined) { | ||
| const eventSource = isHydrated(this.#hydrationState) | ||
| ? this.#hydrationState.innerNode.anchorNode.events | ||
| : this.#hydrationState.innerNode.events; | ||
| this.#eventBuffer = new KernelEventBuffer(eventSource); | ||
| } | ||
| return this.#eventBuffer; | ||
|
Josmithr marked this conversation as resolved.
|
||
| } | ||
|
|
||
|
|
@@ -244,7 +253,7 @@ export class TreeNodeKernel { | |
| off(); | ||
| } | ||
| } | ||
| this.#eventBuffer.dispose(); | ||
| this.#eventBuffer?.dispose(); | ||
| // TODO: go to the context and remove myself from withAnchors | ||
| } | ||
|
|
||
|
|
@@ -540,6 +549,8 @@ class KernelEventBuffer implements Listenable<KernelEvents> { | |
| } | ||
|
|
||
| public on(eventName: keyof KernelEvents, listener: KernelEvents[typeof eventName]): Off { | ||
| this.#assertNotDisposed(); | ||
|
|
||
| // Lazily bind event listeners to the source. | ||
| // If we do not have any existing listeners for this event, then we need to bind to the source. | ||
| if (!this.#events.hasListeners(eventName)) { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.