Repository navigation
perf: generate rendered component markup on demand - #1979
Open
swisscoder wants to merge 1 commit into
Open
swisscoder wants to merge 1 commit into
swisscoder wants to merge 1 commit into
Conversation
A component returned to a test (the cut, or one found via FindComponent or FindComponents) is notified through UpdateParents once per changed child in a render batch. Each notification regenerated the full markup of its subtree with Htmlizer.GetHtml, so the same markup was rebuilt many times per batch while the test read it at most once. Measured in a real-world suite: one state change on a page with ~100 KB of markup regenerated the page markup 44 to 284 times; a CPU profile of the test attributed ~40 % of its time to UpdateMarkup. UpdateState now only marks the markup as stale and still raises OnMarkupUpdated, so the WaitFor helpers and other subscribers keep their semantics: they read Markup, which generates it once. Generation runs under the renderer's render tree lock (ReadRenderTree), like FindComponents, so no batch can change the tree while it is read; the lock is re-entrant for handlers invoked from UpdateDisplayAsync.
Author
|
@dotnet-policy-service agree |
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Pull request description
A component returned to a test (the
cut, or one found viaFindComponent/FindComponents) is notified throughUpdateParentsonce per changed child in a render batch. On every notificationRenderedComponent.UpdateStateregenerated the full markup of its subtree withHtmlizer.GetHtml, so the same markup was rebuilt many times per batch, while the test reads it at most once.This PR generates the markup on demand:
UpdateStateonly marks it as stale, andMarkup(and with itNodesandFind) generates it on the next read.Why: in a real-world test suite (Blazor pages with ~100 KB of rendered markup), a single state change regenerated the page markup 44 to 284 times; a CPU profile attributed ~40 % of the test time to
UpdateMarkup. Measured with this patch on two affected test projects (same machine, alternating runs, all tests green):Semantics kept:
OnMarkupUpdatedis still raised after each render that changes the markup, so theWaitFor*helpers and other subscribers behave as before: they readMarkup, which generates it once.BunitRenderer.ReadRenderTree, the same lockFindComponentsuses), so no render batch can change the tree while it is read. The lock is re-entrant for handlers invoked fromUpdateDisplayAsync. After the renderer is disposed, the last generated markup is returned.Tests: four new tests in
RenderedComponentTest(repeated reads return the same instance; markup reflects the last of several unread renders;OnMarkupUpdatedsubscribers read the updated markup; a parent's markup reflects a render of a child found viaFindComponent). Full suite green locally on net8.0, net9.0, net10.0 and net11.0.PR meta checklist
mainbranch for codeor targeted at
stablebranch for documentation that is live on bunit.dev.Code PR specific checklist