Description
The Blazor SSR implementation of the TechEmpower Fortunes benchmark is quite a bit slower than the Razor Pages implementation.
When I initially investigated performance of Blazor SSR vs. Razor Pages for the Fortunes benchmark, the difference was much smaller (single-digit percentage). @sebastienros tracked down the drop to this commit during 8.0.0-preview.7, which suggests that the underlying cause might be the anti-forgery middleware (#50065).
The crank output comparing the Blazor SSR implementation vs. returning RazorComponentResult
directly from MapGet
vs. Razor Pages seems to indicate that even with anti-forgery middleware removed from the picture (when returning RazorComponentResult
directly) the performance is still ~25% lower than Razor Pages (but about 2.4x faster than using MapRazorComponents
).
crank output
| db | fortunes-blazor | fortunes-blazor-direct | | fortunes-razorpages | |
| -------------------- | --------------- | ---------------------- | ------- | ------------------- | -------- |
| Max CPU Usage (%) | 11 | 20 | +81.82% | 22 | +100.00% |
| Max Cores usage (%) | 308 | 552 | +79.22% | 628 | +103.90% |
| Max Working Set (MB) | 51 | 51 | 0.00% | 53 | +3.92% |
| Build Time (ms) | 1,169 | 1,484 | +26.95% | 1,221 | +4.45% |
| Start Time (ms) | 1,102 | 1,090 | -1.09% | 1,150 | +4.36% |
| Published Size (KB) | 369,863 | 369,863 | 0.00% | 369,863 | 0.00% |
| application | fortunes-blazor | fortunes-blazor-direct | | fortunes-razorpages | |
| --------------------------------------- | --------------- | ---------------------- | -------- | ------------------- | -------- |
| Max CPU Usage (%) | 99 | 99 | 0.00% | 98 | -1.01% |
| Max Cores usage (%) | 2,766 | 2,760 | -0.22% | 2,744 | -0.80% |
| Max Working Set (MB) | 1,440 | 1,320 | -8.33% | 555 | -61.46% |
| Max Private Memory (MB) | 2,053 | 2,058 | +0.24% | 1,299 | -36.73% |
| Build Time (ms) | 3,792 | 3,295 | -13.11% | 3,988 | +5.17% |
| Start Time (ms) | 264 | 246 | -6.82% | 288 | +9.09% |
| Published Size (KB) | 103,944 | 103,944 | 0.00% | 103,950 | +0.01% |
| Symbols Size (KB) | 29 | 29 | 0.00% | 28 | -3.45% |
| .NET Core SDK Version | 8.0.100 | 8.0.100 | | 8.0.100 | |
| Max CPU Usage (%) | 99 | 99 | -0.04% | 98 | -0.35% |
| Max Working Set (MB) | 1,493 | 1,390 | -6.91% | 582 | -61.04% |
| Max GC Heap Size (MB) | 777 | 1,081 | +39.21% | 347 | -55.34% |
| Size of committed memory by the GC (MB) | 1,110 | 666 | -40.00% | 461 | -58.52% |
| Max Number of Gen 0 GCs / sec | 9.00 | 18.00 | +100.00% | 10.00 | +11.11% |
| Max Number of Gen 1 GCs / sec | 2.00 | 2.00 | 0.00% | 5.00 | +150.00% |
| Max Number of Gen 2 GCs / sec | 1.00 | 1.00 | 0.00% | 1.00 | 0.00% |
| Max Gen 0 GC Budget (MB) | 1,393 | 1,349 | -3.16% | 353 | -74.66% |
| Max Time in GC (%) | 3.00 | 3.00 | 0.00% | 2.00 | -33.33% |
| Max Gen 0 Size (B) | 93,333,264 | 6,348,152 | -93.20% | 6,275,496 | -93.28% |
| Max Gen 1 Size (B) | 43,891,864 | 41,113,960 | -6.33% | 41,080,384 | -6.41% |
| Max Gen 2 Size (B) | 40,472,144 | 38,131,472 | -5.78% | 12,100,520 | -70.10% |
| Max LOH Size (B) | 187,376 | 187,376 | 0.00% | 188,008 | +0.34% |
| Max POH Size (B) | 1,185,400 | 1,206,000 | +1.74% | 1,214,240 | +2.43% |
| Max Allocation Rate (B/sec) | 3,079,377,968 | 5,908,769,424 | +91.88% | 3,196,734,984 | +3.81% |
| Max GC Heap Fragmentation (%) | 5,015% | 882% | -82.41% | 3,130% | -37.57% |
| # of Assemblies Loaded | 118 | 118 | 0.00% | 133 | +12.71% |
| Max Exceptions (#/s) | 472 | 464 | -1.69% | 468 | -0.85% |
| Max Lock Contention (#/s) | 270 | 429 | +58.89% | 386 | +42.96% |
| Max ThreadPool Threads Count | 48 | 48 | 0.00% | 48 | 0.00% |
| Max ThreadPool Queue Length | 205 | 196 | -4.39% | 129 | -37.07% |
| Max ThreadPool Items (#/s) | 254,972 | 603,950 | +136.87% | 365,324 | +43.28% |
| Max Active Timers | 12 | 19 | +58.33% | 8 | -33.33% |
| IL Jitted (B) | 859,557 | 773,843 | -9.97% | 737,378 | -14.21% |
| Methods Jitted | 9,893 | 8,930 | -9.73% | 8,445 | -14.64% |
| Load Working Set - P90 (MB) | 718 | 677 | -5.71% | 554 | -22.84% |
| Load CPU Usage - P90 (%) | 98 | 97 | -1.02% | 96 | -2.04% |
| load | fortunes-blazor | fortunes-blazor-direct | | fortunes-razorpages | |
| ----------------------- | --------------- | ---------------------- | -------- | ------------------- | -------- |
| Max CPU Usage (%) | 13 | 26 | +100.00% | 25 | +92.31% |
| Max Cores usage (%) | 354 | 733 | +107.06% | 705 | +99.15% |
| Max Working Set (MB) | 48 | 46 | -4.17% | 46 | -4.17% |
| Max Private Memory (MB) | 358 | 358 | 0.00% | 358 | 0.00% |
| Build Time (ms) | 3,162 | 3,311 | +4.71% | 3,096 | -2.09% |
| Start Time (ms) | 0 | 0 | | 0 | |
| Published Size (KB) | 72,511 | 72,511 | 0.00% | 72,511 | 0.00% |
| Symbols Size (KB) | 0 | 0 | | 0 | |
| .NET Core SDK Version | 8.0.100 | 8.0.100 | | 8.0.100 | |
| First Request (ms) | 401 | 381 | -4.99% | 353 | -11.97% |
| Requests/sec | 63,172 | 154,367 | +144.36% | 202,153 | +220.00% |
| Requests | 951,310 | 2,330,895 | +145.02% | 3,052,536 | +220.88% |
| Mean latency (ms) | 4.61 | 1.77 | -61.61% | 1.39 | -69.85% |
| Max latency (ms) | 76.03 | 31.77 | -58.21% | 36.80 | -51.60% |
| Bad responses | 0 | 0 | | 0 | |
| Socket errors | 0 | 0 | | 0 | |
| Read throughput (MB/s) | 109.47 | 220.09 | +101.05% | 305.38 | +178.96% |
| Latency 50th (ms) | 3.55 | 1.52 | -57.18% | 1.13 | -68.17% |
| Latency 75th (ms) | 5.64 | 2.08 | -63.12% | 1.66 | -70.57% |
| Latency 90th (ms) | 8.60 | 2.88 | -66.51% | 2.23 | -74.07% |
| Latency 99th (ms) | 20.73 | 6.43 | -68.98% | 6.76 | -67.39% |