Skip to content

[Pre4] Blazor WASM performance profiling #35243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions aspnetcore/blazor/performance/app-download-size.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,21 @@ After an app is deployed, verify that the app serves compressed files. Inspect t

Blazor WebAssembly's runtime includes the following .NET features that can be disabled for a smaller payload size.

Blazor WebAssembly carries globalization resources required to display values, such as dates and currency, in the user's culture. If the app doesn't require localization, you may [configure the app to support the invariant culture](xref:blazor/globalization-localization#invariant-globalization), which is based on the `en-US` culture.
Blazor WebAssembly carries globalization resources required to display values, such as dates and currency, in the user's culture. If the app doesn't require localization, you may configure the app to [support the invariant culture](xref:blazor/globalization-localization#invariant-globalization), which is based on the `en-US` culture. Apply the `<InvariantGlobalization>` MSBuild property with a value of `true` in the app's project file (`.csproj`):

```xml
<PropertyGroup>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
```

:::moniker range=">= aspnetcore-8.0"

Adopting [invariant globalization](xref:blazor/globalization-localization#invariant-globalization) only results in using non-localized timezone names. To trim timezone code and data from the app, apply the `<InvariantTimezone>` MSBuild property with a value of `true` in the app's project file:
Adopting [invariant globalization](xref:blazor/globalization-localization#invariant-globalization) only results in using non-localized timezone names. To trim timezone code and data from the app, apply the `<InvariantTimezone>` MSBuild property with a value of `true` in the app's project file (`.csproj`):

```xml
<PropertyGroup>
<InvariantTimezone>true</InvariantTimezone>
<InvariantTimezone>true</InvariantTimezone>
</PropertyGroup>
```

Expand All @@ -89,7 +95,7 @@ A data file is included to make timezone information correct. If the app doesn't

```xml
<PropertyGroup>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
</PropertyGroup>
```

Expand All @@ -101,8 +107,12 @@ Collation information is included to make APIs such as <xref:System.StringCompar

```xml
<PropertyGroup>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
<BlazorWebAssemblyPreserveCollationData>false</BlazorWebAssemblyPreserveCollationData>
</PropertyGroup>
```

:::moniker-end

## Additional resources

[Configuring and hosting .NET WebAssembly applications](https://github.com/dotnet/runtime/blob/main/src/mono/wasm/features.md)
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
---
title: ASP.NET Core Blazor WebAssembly performance profiling and diagnostic counters
title: ASP.NET Core Blazor WebAssembly developer tools performance profiling and diagnostic counters
author: guardrex
description: Learn about performance profiling and diagnostic counters in ASP.NET Core Blazor WebAssembly apps.
description: Learn about developer tools performance profiling and diagnostic counters in ASP.NET Core Blazor WebAssembly apps.
monikerRange: '>= aspnetcore-10.0'
ms.author: riande
ms.custom: mvc
ms.date: 04/16/2025
uid: blazor/performance/profiling
ms.date: 04/29/2025
uid: blazor/performance/developer-tools-profiling
---
# ASP.NET Core Blazor WebAssembly performance profiling and diagnostic counters
# ASP.NET Core Blazor WebAssembly developer tools performance profiling and diagnostic counters

<!-- UPDATE 10.0 - Activate ...

[!INCLUDE[](~/includes/not-latest-version.md)]

-->

This article describes performance profiling tools and diagnostic counters for Blazor WebAssembly apps.
This article describes developer tools performance profiling tools and diagnostic counters for Blazor WebAssembly apps.

## Prerequisite

Expand All @@ -34,10 +34,9 @@ Built-in performance counters are available to track:

* [Ahead-of-time (AOT) compilation](xref:blazor/tooling/webassembly#ahead-of-time-aot-compilation)
* Code interpolation
* [JIT (Just-In-Time) interpolation](https://developer.mozilla.org/docs/Glossary/Just_In_Time_Compilation)
* Call specification (":::no-loc text="callspec":::", sequence and timing of function calls) and instrumentation

Enable integration with the browser's developer tools profiler using the `<WasmProfilers>` property in the app's project file (`.csproj`). Include the additional properties in the following table.
The MSBuild properties in the following table enable profiler integration.

Property | Default | Set value to&hellip; | Description
--- | :---: | :---: | ---
Expand All @@ -48,8 +47,12 @@ Property | Default | Set value to&hellip; | Description
`<WasmNativeDebugSymbols>` | `true` | `true` | Controls building with native debug symbols.
`<WasmBuildNative>` | `false` | `true` | Controls building the native executable.

Enabling profilers has negative size and performance impact, so don't publish an app for production with profilers enabled. In the following example, a condition is set on a property group section that only enables profiling when the app is built with `/p:ProfilingEnabled=true` (.NET CLI) or `<ProfilingEnabled>true</ProfilingEnabled>` in a Visual Studio publish profile.

In the app's project file (`.csproj`):

```xml
<PropertyGroup>
<PropertyGroup Condition="'$(ProfilingEnabled)' == 'true'">
<WasmProfilers>browser;</WasmProfilers>
<RunAOTCompilation>true</RunAOTCompilation>
<RunAOTCompilationAfterBuild>true</RunAOTCompilationAfterBuild>
Expand All @@ -59,7 +62,15 @@ Property | Default | Set value to&hellip; | Description
</PropertyGroup>
```

Add Blazor start configuration in `wwwroot/index.html`, using the [fingerprinted location of the Blazor WebAssembly script](xref:blazor/fundamentals/static-files#fingerprint-client-side-static-assets-in-standalone-blazor-webassembly-apps). In the following example, the `sampleIntervalMs` option is set to 10 seconds, which is the default setting if `sampleIntervalMs` isn't specified:
Setting WebAssembly profilers with `<WasmProfilers>browser;</WasmProfilers>` doesn't require AOT (`<RunAOTCompilation>`/`<RunAOTCompilationAfterBuild>` set to `false` or removed from the preceding properity group).

The browser developer tools profiler can be used with AOT (`<RunAOTCompilation>`/`<RunAOTCompilationAfterBuild>` set to `true`) and without WebAssembly profilers (`<WasmProfilers>browser;</WasmProfilers>` removed from the preceding property group).

To see AOT method names in the developer tools console, install [DWARF chrome extension](https://chromewebstore.google.com/detail/cc++-devtools-support-dwa/pdcpmagijalfljmkmjngeonclgbbannb).

## Set the sample interval

To set the sample interval, add the following Blazor start configuration in `wwwroot/index.html` and add `autostart="false"` to the Blazor `<script>` tag. In the following example, the `sampleIntervalMs` option is set to 10 seconds, which is the default setting if `sampleIntervalMs` isn't specified:

```html
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"
Expand Down Expand Up @@ -105,57 +116,12 @@ Configure `callSpec` in `browserProfilerOptions`. Replace the `{APP NAMESPACE}`
</script>
```

## Log profiling for memory troubleshooting

Enable integration with the log profiler using the `<WasmProfilers>` and `<WasmBuildNative>` properties in the app's project file (`.csproj`):

```xml
<PropertyGroup>
<WasmProfilers>log</WasmProfilers>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
```

To trigger a heap shot, add the following, where the `{APP NAMESPACE}` placeholder is the app's namespace:

```csharp
namespace {APP NAMESPACE};

public class Profiling
{
[JSExport]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void TakeHeapshot() { }
}
```

Invoke `TakeHeapshot` to create a memory heap shot and flush the contents of the profile to the file system. Download the resulting `.mpld` file to analyze the data.

## EventPipe profiler

[EventPipe](/dotnet/core/diagnostics/eventpipe) is a runtime component used to collect tracing data, similar to [ETW](/windows/win32/etw/event-tracing-portal) and [perf_events](https://wikipedia.org/wiki/Perf_%28Linux%29).

* Manual testing
* Browser developer tools: Download the `.json` output file, open the file in Visual Studio, and find the expected method calls.
* [`dotnet-trace`](/dotnet/core/diagnostics/dotnet-trace): Open the `.nettrace` output file in Visual Studio and find the expected method calls.
* Web-based testing
* Use the JavaScript API to obtain a [promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) of NetTrace (`.nettrace`) bytes.
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected method calls.

Built-in performance counters are available to track:

* [Ahead-of-time (AOT) compilation](xref:blazor/tooling/webassembly#ahead-of-time-aot-compilation)
* Code interpolation
* [JIT (Just-In-Time) interpolation](https://developer.mozilla.org/docs/Glossary/Just_In_Time_Compilation)

## GC (Garbage Collector) dumps

* Manual testing
* Browser developer tools: Download the `.json` output file, open the file in Visual Studio, and find the expected classes.
* [`dotnet-gcdump` (`collect`/convert` options)](/dotnet/core/diagnostics/dotnet-gcdump): To view the captured GC dump files, see [View the GC dump captured from dotnet-gcdump](/dotnet/core/diagnostics/dotnet-gcdump#view-the-gc-dump-captured-from-dotnet-gcdump).
* Web-based testing
* Use the JavaScript API to obtain a [promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) of NetTrace (`.nettrace`) bytes.
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected classes.

Expand All @@ -165,7 +131,6 @@ Built-in performance counters are available to track:
* Browser developer tools: Download the `.json` output file, open the file in Visual Studio, and find the expected counters.
* [`dotnet-counters collect`](/dotnet/core/diagnostics/dotnet-counters): Open the `.csv`/`.json` output file in Visual Studio and find the expected counters.
* Web-based testing
* Use the JavaScript API to obtain a [promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise) of NetTrace (`.nettrace`) bytes.
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected counters.

Expand Down
119 changes: 119 additions & 0 deletions aspnetcore/blazor/performance/event-pipe-profiling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: ASP.NET Core Blazor WebAssembly Event Pipe performance profiling and diagnostic counters
author: guardrex
description: Learn about Event Pipe performance profiling and diagnostic counters in ASP.NET Core Blazor WebAssembly apps.
monikerRange: '>= aspnetcore-10.0'
ms.author: riande
ms.custom: mvc
ms.date: 04/29/2025
uid: blazor/performance/event-pipe-profiling
---
# ASP.NET Core Blazor WebAssembly Event Pipe performance profiling and diagnostic counters

<!-- UPDATE 10.0 - Activate ...

[!INCLUDE[](~/includes/not-latest-version.md)]

-->

This article describes Event Pipe performance profiling tools and diagnostic counters for Blazor WebAssembly apps.

## Prerequisite

Install the [.NET WebAssembly build tools](xref:blazor/tooling/webassembly#net-webassembly-build-tools):

```dotnetcli
dotnet workload install wasm-tools
```

## EventPipe profiler

[EventPipe](/dotnet/core/diagnostics/eventpipe) is a runtime component used to collect tracing data, similar to [ETW](/windows/win32/etw/event-tracing-portal) and [perf_events](https://wikipedia.org/wiki/Perf_%28Linux%29).

* Manual testing
* Browser developer tools: Download the `.nettrace` output file, open the file in Visual Studio, and find the expected method calls.
* [`dotnet-trace`](/dotnet/core/diagnostics/dotnet-trace): Open the `.nettrace` output file in Visual Studio and find the expected method calls.
* Web-based testing
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected method calls.

Built-in performance counters are available to track:

* [Ahead-of-time (AOT) compilation](xref:blazor/tooling/webassembly#ahead-of-time-aot-compilation)
* Code interpolation

## GC (Garbage Collector) dumps

* Manual testing
* Browser developer tools: Download the `.json` output file, open the file in Visual Studio, and find the expected classes.
* [`dotnet-gcdump` (`collect`/convert` options)](/dotnet/core/diagnostics/dotnet-gcdump): To view the captured GC dump files, see [View the GC dump captured from dotnet-gcdump](/dotnet/core/diagnostics/dotnet-gcdump#view-the-gc-dump-captured-from-dotnet-gcdump).
* Web-based testing
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected classes.

## Counters trace

* Manual testing
* Browser developer tools: Download the `.json` output file, open the file in Visual Studio, and find the expected counters.
* [`dotnet-counters collect`](/dotnet/core/diagnostics/dotnet-counters): Open the `.csv`/`.json` output file in Visual Studio and find the expected counters.
* Web-based testing
* Upload the file via HTTP.
* Parse and validate that the trace contains the expected counters.

## .NET Core Diagnostics Client Library example

Parse and validate NetTrace (`.nettrace`) messages using the .NET Core Diagnostics Client Library:

* [`dotnet/diagnostics` GitHub repository](https://github.com/dotnet/diagnostics)
* [`Microsoft.Diagnostics.NETCore.Client` NuGet package](https://www.nuget.org/packages/Microsoft.Diagnostics.NETCore.Client)

For more information, see the [.NET Core diagnostics documentation](/dotnet/core/diagnostics/) and the [`IpcMessage` API (reference source)](https://github.com/dotnet/diagnostics/blob/main/src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcMessage.cs).

[!INCLUDE[](~/includes/aspnetcore-repo-ref-source-links.md)]

The following example:

* Collects a GC (Garbage Collector) dump of the live .NET process.
* Collects performance counters for 60 seconds.
* Collects CPU counters for 60 seconds.

The MSBuild properties in the following table enable profiler integration.

Property | Default | Set value to&hellip; | Description
--- | :---: | :---: | ---
`<WasmPerfTracing>` | `false` | `true` | Controls diagnostic server tracing.
`<WasmPerfInstrumentation>` | `false` | `true` | Controls CPU sampling instrumentation for diagnostic server. Not necessary for memory dump or counters. **Makes the app execute slower. Only set this to `true` for performance profiling.
`<MetricsSupport>` | `false` | `true` | Controls `System.Diagnostics.Metrics` support. For more information, see the [`System.Diagnostics.Metrics` namespace](/dotnet/api/system.diagnostics.metrics).
`<EventSourceSupport>` | `false`| `true` | Controls `EventPipe` support. For more information, see [Diagnostics and instrumentation: Observability and telemetry](/dotnet/core/deploying/native-aot/diagnostics#observability-and-telemetry).

Enabling profilers has negative size and performance impact, so don't publish an app for production with profilers enabled. In the following example, a condition is set on a property group section that only enables profiling when the app is built with `/p:ProfilingEnabled=true` (.NET CLI) or `<ProfilingEnabled>true</ProfilingEnabled>` in a Visual Studio publish profile.

In the app's project file (`.csproj`):

```xml
<PropertyGroup Condition="'$(ProfilingEnabled)' == 'true'">
<WasmPerfTracing>true</WasmPerfTracing>
<WasmPerfInstrumentation>true</WasmPerfInstrumentation>
<MetricsSupport>true</MetricsSupport>
<EventSourceSupport>true</EventSourceSupport>
</PropertyGroup>
```

The [`Timing-Allow-Origin` HTTP header](https://developer.mozilla.org/docs/Web/HTTP/Reference/Headers/Timing-Allow-Origin) allows for more precise time measurements.

Browser developer tools console calls in the following example that trigger profiling:

* `collectGcDump`: Collect a GC (Garbage Collector) dump.
* `collectPerfCounters(durationSeconds)`: Collect general performance counters.
* `collectCpuSamples(durationSeconds)`: Collect CPU performance counters.

```javascript
globalThis.getDotnetRuntime(0).collectGcDump();
globalThis.getDotnetRuntime(0).collectPerfCounters({durationSeconds: 60});
globalThis.getDotnetRuntime(0).collectCpuSamples({durationSeconds: 60});
```

## Additional resources

* [What diagnostic tools are available in .NET Core?](/dotnet/core/diagnostics/)
* [.NET diagnostic tools](/dotnet/core/diagnostics/tools-overview)
6 changes: 4 additions & 2 deletions aspnetcore/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,10 @@ items:
uid: blazor/performance/app-download-size
- name: JavaScript interop
uid: blazor/performance/js-interop
- name: WebAssembly profiling
uid: blazor/performance/profiling
- name: WebAssembly developer tools profiling
uid: blazor/performance/developer-tools-profiling
- name: WebAssembly Event Pipe profiling
uid: blazor/performance/event-pipe-profiling
- name: Test components
uid: blazor/test
- name: Progressive Web Applications
Expand Down
Loading