Repository navigation
Conversation
ComponentState eagerly created two RenderTreeBuilders (each with a RenderTreeFrameArrayBuilder and a Stack<int>) so that it could diff the new output against the previous one. Many components only ever render once, for example every component during static SSR, so the second builder was never used. Write the first render directly into CurrentRenderTree, diffing it against an empty tree, and only create the second builder when the component re-renders. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the change that defers allocation of the second render-tree builder.
No open issues 🎉
What changed in this PR
This PR reduces per-component memory use in Blazor by allocating the second RenderTreeBuilder only when a component renders again, avoiding that allocation for components that render once.
Changes:
- Writes the first render directly into the current tree and diffs it against an empty tree, preserving subsequent-render behavior.
- Adds regression tests for first-render failures, empty-tree cleanup, and successful recovery.
- Adds HTML-rendering benchmarks to measure rendering allocations and HTML-writing performance.
| File | Description |
|---|---|
src/Components/Components/test/RendererTest.cs |
Tests first-render failure cleanup and later render recovery. |
src/Components/Components/src/Rendering/ComponentState.cs |
Defers the second builder allocation and handles first-render cleanup and nullable disposal. |
src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj |
References Components.Web for HTML-rendering benchmarks. |
src/Components/Components/perf/HtmlRendererBenchmark.cs |
Adds full-render and HTML-writing benchmarks using a component-based table. |
Review effort: Balanced
|
Compliance check: no public API changes. Rendered output, batches and diffs are identical, and first-render exceptions still leave 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/azp run aspnetcore-components-e2e Rerunning e2e. Both failures are known flaky tests unrelated to this change, per Build Analysis: 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
No pipelines are associated with this pull request. |
|
/azp run aspnetcore-components-e2e Rerunning e2e. Only the Mono-server leg failed; CoreCLR and Mono/WebAssembly passed with this change. Failing tests, per Build Analysis:
If the QuickGrid test fails again, I'll investigate it as potentially related. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
No pipelines are associated with this pull request. |
|
/review |
ComponentStateeagerly creates twoRenderTreeBuilders, each with its ownRenderTreeFrameArrayBuilderandStack<int>, so it can diff new output against the previous output. Many components only ever render once, and every component does during static SSR, so the second builder is never used. It stays allocated and retained for the component's lifetime, which in a Blazor Server circuit can be long.Description
_nextRenderTreeis now created only when a component re-renders.CurrentRenderTree(which is empty at that point) and diffed against an emptyArrayRange.AssertTreeIsValid,CurrentRenderTreeis cleared. It keeps representing the previous (empty) output, as before.Benchmarks
This adds the same
HtmlRendererBenchmarkas #70151, with identical files.RenderAndWriteHtmlcreates anHtmlRenderer, renders 101 components (a 100-row table whose rows are child components), and writes the HTML.Local BenchmarkDotNet, InProcess, 3 interleaved rounds against main:
RenderAndWriteHtmlallocatedRenderAndWriteHtmltimeGC.GetTotalMemory(true)with 50 live renderers)Tests
Microsoft.AspNetCore.Components.Tests: 1305 passed, 8 skipped.ExceptionDuringFirstRender_LeavesRenderTreeEmpty_AndLaterRenderSucceedsInvalidRenderTreeDuringFirstRender_LeavesRenderTreeEmptyMicrosoft.AspNetCore.Components.Web.Tests: 330 passed.Microsoft.AspNetCore.Components.Endpoints.Tests: 883 passed.Microsoft.AspNetCore.Components.Server.Tests: 394 passed.🤖 Posted by ASP.NET Core Perf 2 (AI Agent)