Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance

Every allocation on the hot path of mediator.Send(request):

Warm path (cache populated, 3 behaviors)

Send<TResponse>(request, ct)                          ALLOCS
├─ ArgumentNullException.ThrowIfNull                  0
├─ WrapperCache.GetOrCreate → cache hit               0
└─ wrapper.Handle(request, provider, ct)
   ├─ GetRequiredService<IRequestHandler<..>>()       0-N (DI)
   ├─ GetBehaviors(provider)
   │   └─ GetServices<...>().ToArray()                1
   │      (no-behavior flag cached per-provider via CWT)
   ├─ BuildPipeline(handler, behaviors)
   │   ├─ handler.Handle delegate                     1
   │   └─ × N behaviors:
   │       new BehaviorChain                          1 each
   │       chain.Invoke delegate                      1 each
   └─ pipeline(request, ct)                           0
ScenarioAllocations per Send (steady-state)
No behaviors1 (handler delegate only; no-behavior flag cached per-provider, GetServices+ToArray skipped)
1 behavior4 (handler delegate + ToArray() + BehaviorChain + invoke delegate)
3 behaviors8 (handler delegate + ToArray() + 3×(BehaviorChain + invoke delegate))

All are small, short-lived gen-0 objects.

Cold path (first call per type)

On the first Send for a given request type, the wrapper factory runs once:

Before (Activator.CreateInstance)After (compiled expression)
Reflection-based constructionCached delegate invocation

The factory result is cached in ConcurrentDictionary — subsequent calls pay zero overhead.

Publishes with zero handlers

BeforeAfter
async Task — class state machine on heapasync ValueTask — struct state machine on stack

When no handlers are registered, the method completes synchronously. ValueTask keeps the state machine on the stack.

What was removed from the hot path

RemovedSaving
is not TRequest type check + throw path1 branch + 1 dead code path per Send
Lambda closures per behavior1 heap closure per behavior layer
Activator.CreateInstance reflectionReflection → delegate call
async Task class state machineHeap → stack allocation
Enum.IsDefined reflectionRange check (JIT-inlinable)