5 frames, 3.2x faster: I measured .NET 11 runtime async
.NET 11 shipped today, and its headline runtime feature is Runtime Async — the runtime itself now manages async suspension instead of the compiler emitting state-machine classes. Microsoft's docs show a stack trace shrinking from 13 frames to 5. I installed SDK 11.0.100 on this VM and ran the docs' own sample: my trace went from 17 frames to 5, and a completed-task await loop got 3.2x faster.
The setup
Ubuntu 24.04, 4 vCPUs, AMD EPYC 9D25 (AVX2, AVX-512, BMI2, FMA — comfortably past .NET 11's new x86-64-v2 minimum baseline). SDK 11.0.100 via the dotnet-install script, Release build, same binary run three times per configuration, medians reported. Runtime Async is a preview feature, opted into with one line in the project file:
<Features>runtime-async=on</Features> One wrinkle before the numbers: the what's-new page still says "last updated for release candidate 2" even though 11.0.100 is out. The docs lag the release — keep that in mind when a claim below says "preview."
The stack trace: 17 frames became 5
I ran the docs' exact sample — three nested async methods awaiting Task.CompletedTask, with a StackTrace captured in the innermost one. Without Runtime Async, the trace is 17 frames of state-machine noise:
Program.<<Main>$>g__InnerAsync|0_2>d.MoveNext System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start Program.<<Main>$>g__InnerAsync|0_2 Program.<<Main>$>g__MiddleAsync|0_1>d.MoveNext System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start Program.<<Main>$>g__MiddleAsync|0_1 Program.<<Main>$>g__OuterAsync|0_0>d.MoveNext System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start Program.<<Main>$>g__OuterAsync|0_0 <<Main>$>d__0.MoveNext System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start Program.<Main>$ Program.<Main> With <Features>runtime-async=on</Features>, the same program prints 5 frames — the real call chain, nothing else:
Program.<<Main>$>g__InnerAsync|0_2 Program.<<Main>$>g__MiddleAsync|0_1 Program.<<Main>$>g__OuterAsync|0_0 Program.<Main>$ Program.<Main> The docs claim 13 → 5 for their sample; mine is 17 → 5 because my chain has one extra frame layer (top-level Main adds its own d__0 state machine). The endpoint is identical: 5 frames, and the count was stable across all three runs in each configuration. This is the number that matters for profilers and debuggers — live stack traces finally show the methods you wrote.
The throughput: 9.4M to 29.9M awaits per second
Stack traces are the visible win; the docs also promise lower overhead. I measured two microbenchmarks, 2,000,000 iterations each, three runs per configuration, medians:
- CHAIN — an async method that awaits
Task.CompletedTaskthree times in a row, called in a loop. - LEAF — a
ValueTask<long>leaf method awaiting once and returning a value.
benchmark runtime-async OFF (median of 3) ON (median of 3) speedup CHAIN 9.78 Mops/s (7.54 / 9.78 / 10.17) 22.84 Mops/s (21.74 / 33.54 / 22.84) 2.3x LEAF 9.41 Mops/s (7.50 / 9.41 / 9.63) 29.89 Mops/s (29.89 / 28.56 / 48.11) 3.2x These are awaits of already-completed tasks — the best case for Runtime Async, since the old path pays full state-machine setup per call and the new path mostly doesn't. Real workloads with actual I/O in the await will see less. And this is a noisy shared 4-vCPU VM: individual runs swung from 21.7 to 33.5 Mops/s on the same binary. The direction is consistent across all six runs; the exact multiplier is not a promise.
The other claim I checked: BigMul is one instruction now
The same what's-new page says Math.BigMul(long, long, out long) is "now significantly faster on x64" because the JIT emits a single MUL r/m64 instead of calling a helper. I dumped the JIT output with DOTNET_JitDisasm (full optimizations, tiered compilation off). The whole method is 11 bytes:
; Assembly listing for method Program:<<Main>$>g__BigMulLow|0_0(long):long (FullOpts) G_M000_IG02: lea rax, [rdi+0x01] imul rdx:rax, rdi add rax, rdx G_M000_IG03: ret ; Total bytes of code 11 One imul producing the 128-bit result (low in rax, high in rdx), no helper call. The claim checks out — with one footnote I tripped over while writing the test: BigMul returns the high half and puts the low half in the out parameter. My first benchmark accumulated only the return value and got a sum of exactly 0, which looked like a JIT bug until I read the signature. The verified checksum: summing i * (i + 1) for i = 1..50,000,000 through BigMul gives -4525695843177100544 as a signed 64-bit value — identical to the same sum computed in Python with arbitrary precision and reduced mod 2^64. The timing comparison (50M iterations, BigMul ~93 ms vs plain 64-bit multiply 33–88 ms) is too noisy on this box to call, so I won't: the codegen is the verified part.
What I'd actually take from this release
Runtime Async is the real story, and it's a preview for a reason — Microsoft's own note says the runtime libraries are already compiled with it, so the dogfooding is happening, but your app opts in explicitly. The 5-frame stack trace is the thing you'll notice on day one; the 2–3x on completed-task awaits is the thing that shows up in a hot loop. The baseline bump (x86-64-v2 minimum, R2R targeting v3) is the quiet breaking change: .NET 11 will now refuse to run on CPUs older than roughly 2013, and print "The current CPU is missing one or more of the baseline instruction sets." If your fleet has anything that old, that's the line in the release notes to read first.
Sources: Microsoft Learn, "What's new in .NET 11 runtime" (November 10, 2026; page still marked "last updated for release candidate 2"); .NET 11 SDK 11.0.100 installed from dotnet-install.sh on Ubuntu 24.04, AMD EPYC 9D25. All numbers are medians of three runs of the same Release binary on a shared 4-vCPU VM; the disassembly was captured with DOTNET_JitDisasm and DOTNET_TieredCompilation=0.