Repository navigation
Conversation
Each RenderTreeFrame (20 bytes) and RenderTreeEdit (16 bytes) was previously serialized through 4-7 separate BinaryWriter.Write calls (each a virtual Stream.Write), plus per-element writes for the disposed component/event handler ID arrays. Assemble each frame/edit in a stack buffer and write it once, and bulk-write the int/ulong arrays on little-endian platforms. The output bytes are unchanged. Adds RenderBatchWriterBenchmark to the Components perf project. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The missing frozen review bundle and existing-feedback snapshot prevent completion of revision-provenance checks and prior-feedback deduplication.
No open issues 🎉
What changed in this PR
This PR reduces stream writes in the render-batch serializer shared by Blazor Server, WebView, and WebAssembly out-of-process rendering, while preserving the existing wire format.
Changes:
- Assemble each frame and edit in a fixed-size stack buffer for a single write.
- Bulk-write disposed ID arrays on little-endian platforms, retaining per-element writes on big-endian platforms.
- Add large- and small-batch serialization benchmarks and the project configuration needed to compile the shared writer.
| File | Description |
|---|---|
src/Components/Shared/src/RenderBatchWriter.cs |
Consolidates frame, edit, and ID-array writes; replaces string writes with index lookup. |
src/Components/Components/perf/RenderBatchWriterBenchmark.cs |
Adds table-render and counter-style update benchmarks. |
src/Components/Components/perf/Microsoft.AspNetCore.Components.Performance.csproj |
Includes shared serialization sources and enables their compilation settings. |
Review effort: Balanced
|
/azp run aspnetcore-components-e2e The only failure is 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
No pipelines are associated with this pull request. |
|
/azp run aspnetcore-components-e2e 🤖 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: the CoreCLR e2e leg failed only 🤖 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. |
|
Compliance check: no public API or PublicAPI.*.txt changes. The wire format is byte-identical. I temporarily instrumented every 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
…rshal in RenderBatchWriter Edits and frames are assembled in a private ArrayPool-rented scratch buffer (returned in Dispose) and int/ulong arrays are encoded in chunks with BinaryPrimitives. Wire format is unchanged; adds a multi-chunk test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
CI on b447d02 failed only in Pushed empty commit f7c06cf to rerun CI. 🤖 Posted by ASP.NET Core Perf 2 (AI Agent) |
|
/review |
Write RenderBatchWriter frames and edits as single fixed-size records
Serialize each render batch frame and edit with a single stream write (~1.8× faster for large batches)
Description
RenderBatchWriteris shared by Blazor Server, WebView and WebAssembly out-of-process rendering. It serialized every 20-byteRenderTreeFramethrough 4–7 separateBinaryWriter.Writecalls, and every 16-byteRenderTreeEditthrough 4. Each call is a virtualStream.WriteintoArrayBuilderMemoryStream. The disposed component ID and event handler ID arrays were written one element at a time.This change:
BinaryPrimitivesin a private scratch buffer, then writes it with one call. The buffer is rented fromArrayPool<byte>.Sharedwhen the writer is created and returned (and the field nulled) inDispose. It is never exposed to callers. Every record overwrites all the bytes it emits (frames are cleared first), so no data carries over between records or batches.int/ulongarrays in chunks through the same scratch buffer, usingBinaryPrimitives.Write*LittleEndian.WriteStringwithGetStringIndex, which returns the string-table index so it can be placed in the buffer, and removes the now-unusedWritePadding.There is no
unsafecode, nostackalloc, noMemoryMarshal/Unsafe, and no public API change. The wire format is unchanged byte for byte. I temporarily instrumented everyRenderBatchWriterTestcase to dump its serialized bytes, ran them on main and on this branch, and the SHA-256 of all outputs is identical. That includes the new multi-chunk test.Benchmark
This adds
RenderBatchWriterBenchmarkinsrc/Components/Components/perf:WriteLargeBatch: initial render of a 200-row table, about 2,200 frames and 200 edits.WriteSmallBatch: a counter-style update with 5 edits and 3 frames.Setup: BenchmarkDotNet
--config perflab(InProcess) on a 2 vCPU Linux VM, 3 interleaved rounds.The +8 B per batch is the scratch-buffer field on the writer object; the buffer itself is pooled.
Tests
Microsoft.AspNetCore.Components.Server.Testspasses (395 tests). This includes the byte-exactRenderBatchWriterTestcases that cover every frame type, edits, disposed IDs and string deduplication, plus the newCanIncludeLargeDisposedIdArrays, which spans multiple chunks.Microsoft.AspNetCore.Components.WebViewandMicrosoft.AspNetCore.Components.WebAssembly, which also compile this file, build cleanly.🤖 Posted by ASP.NET Core Perf 2 (AI Agent)