Uh oh!
There was an error while loading. Please reload this page.
[perf-improver] perf: cache distinct processors in AsynchronousMessageBus to eliminate per-drain allocation - #8704
Conversation
…e per-drain allocation - Add _distinctProcessors (IAsyncConsumerDataProcessor[]) and _drainLastReceived (long[]) fields, both initialized once in InitAsync from _consumerProcessor.Values. - Replace the per-call Dictionary<IAsyncConsumerDataProcessor, long> in DrainDataAsync with array-based index tracking; eliminates one dictionary allocation per test run and replaces hash-table lookups with cache-friendly array indexing. - Replace nested foreach loops in DisableAsync and Dispose with single loops over _distinctProcessors, ensuring each processor is completed/disposed exactly once (previously a consumer subscribing to N data types would be visited N times). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR optimizes AsynchronousMessageBus by caching the distinct consumer processors after initialization and reusing that cache for draining, disabling, and disposal.
Changes:
- Adds cached
_distinctProcessorsand_drainLastReceivedarrays initialized inInitAsync. - Replaces per-drain dictionary allocation with indexed array snapshots.
- Updates
DisableAsyncandDisposeto iterate distinct processors once.
Show a summary per file
| File | Description |
|---|---|
src/Platform/Microsoft.Testing.Platform/Messages/AsynchronousMessageBus.cs | Caches distinct processors and reuses arrays for drain/disable/dispose paths. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 2
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Amaury Levé (Evangelink)
commented
May 31, 2026
Copilot address review comments |
… regression test Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
Both review comments addressed in commit
|
Amaury Levé (Evangelink)
commented
May 31, 2026
/review |
Expert-reviewer launched in background for PR #8704. It will post the review directly. |
Uh oh!
There was an error while loading. Please reload this page.
🤖 This is an automated contribution from Perf Improver.
Goal and Rationale
AsynchronousMessageBus.DrainDataAsync()allocated a freshDictionary<IAsyncConsumerDataProcessor, long>on every call solely to track how many items each processor had received between drain rounds. Since the set of processors is fixed afterInitAsync, this dictionary can be replaced with a cached array pair (IAsyncConsumerDataProcessor[]+long[]) allocated once.Additionally,
DisableAsyncandDisposeiterated_dataTypeConsumers.Valueswith a nested loop — because a consumer subscribed to N data types appears in N separate lists, each processor was being completed/disposed N times. Using the deduplicated_distinctProcessorsarray fixes this.Approach
Add two new fields initialized at end of
InitAsync:_distinctProcessors = [.. _consumerProcessor.Values]— the unique set of processors_drainLastReceived = new long[_distinctProcessors.Length]— reusable count snapshotDrainDataAsync: replacenew Dictionary<IAsyncConsumerDataProcessor, long>()+ hash-table lookups with direct array indexing over_distinctProcessors.DisableAsync/Dispose: replace double nestedforeachover_dataTypeConsumers.Valueswith a singleforeachover_distinctProcessors.Performance Evidence
Dictionary<IAsyncConsumerDataProcessor, long>allocated per drainDisableAsync(N consumers × M types)Dispose(N consumers × M types)DrainDataAsyncis called once at the end of every test run. The dictionary allocation and hash operations are eliminated; the reusedlong[]snapshot costs only a stack read per iteration.Methodology: code inspection + allocation diff (directly verifiable from the diff).
Trade-offs
AsynchronousMessageBusinstance (one pointer + one pointer — negligible).DrainDataAsync.DisableAsync/Disposenow callCompleteAddingAsync/Disposeexactly once per processor (vs. once per (processor, data-type) pair previously) — this is strictly more correct.Test Status
Microsoft.Testing.Platform.UnitTests(net8.0): 992 passed, 0 failed, 3 skipped ✅Reproducibility
Add this agentic workflows to your repo
To install this agentic workflow, run