What it is
Plugin.Maui.Performance is a lightweight on-device profiler for .NET MAUI on Android and iOS. It records named timings and automatic hooks so a field build can print a scoreboard like App Startup 1.82 sec without attaching a full IDE profiler.
using var trace = MauiPerformance.Trace("LoadCustomer");
using var checkout = MauiProfile.Scenario("LoadCustomer");Automatic measurement
UseMauiPerformance() turns these hooks on by default. Set Enabled to false in production if you only want timings in debug builds.
| Signal | How |
|---|---|
| Startup time | Process start (or Start()) to the first content page. Recorded as App Startup. |
| Page startup | Page appearing until Loaded. Named from the page title (Home → Home Page). |
| Navigation time | Previous page disappearing until the next appearing (Home Page → Customer Page). |
| API latency | PerformanceDelegatingHandler on HttpClient. Path /customer becomes Customer API. |
| Image loading | Images on each page, aggregated as Image Loading. |
| UI rendering | Next display frame after the page loads (UI Render). Android Choreographer, iOS CADisplayLink. |
| Memory | Working set / managed heap plus free RAM on Android (ActivityManager) and iOS (os_proc_available_memory). |
| Database | Not hooked globally — wrap SQLite (or any store) with TraceDatabase / Measure. |
Named traces
Dispose ends the timing and stores the metric. Cancel() ends it without recording. Resolve IMauiPerformance from dependency injection, or use MauiPerformance.Current.
using var load = MauiPerformance.Trace("LoadCustomer");
using var api = MauiPerformance.TraceApi("Customer API");
using var query = MauiPerformance.TraceDatabase("SQLite Query");
using var image = MauiPerformance.TraceImage("Image Loading");
await MauiPerformance.MeasureAsync("Customer API", () => client.GetStringAsync("/customer"), PerformanceCategory.Api);
MauiPerformance.Measure("SQLite Query", () => db.Query("SELECT * FROM customer"), PerformanceCategory.Database);
MauiPerformance.Record("Home Page", TimeSpan.FromMilliseconds(420), PerformanceCategory.Page);MauiProfile and maui profile
1.0.7 adds MauiProfile as an in-app wrapper for maui profile / Microsoft.Maui.ProfilingHelper. The official CLI captures an EventPipe .nettrace from a Release Android device or iOS simulator. Remembering the stopping-event flags and calling MauiProfilingMarker.Complete() is the unintuitive part. This plugin wraps both sides.
UseMauiPerformance() stops a maui profile startup session on the first display frame (or a named scenario). You do not need a Microsoft.Maui.ProfilingHelper package reference — when maui profile injects the helper, MauiProfile calls Complete() for you.
| API | What it does |
|---|---|
| MauiProfile.IsSession | true when MAUI_PROFILING_HELPER is set or the official marker reports a session. |
| MauiProfile.StartupComplete() | Emits StartupComplete and calls the official marker. |
| MauiProfile.Mark("CartReady") | Named point in the EventPipe trace. |
| MauiProfile.Scenario("Checkout") | Timing + ScenarioComplete; can stop startup. |
| MauiProfile.StartupCommand("android") | Prints the maui profile startup one-liner. |
| MauiProfile.ManualCommand("ios") | Prints maui profile manual for a screen or flow. |
| CliProfile.CompleteOn | When the startup trace stops |
|---|---|
| FirstFrame (default) | After the first display frame following the first page load. |
| FirstPage | When the first content page appears — the same moment as App Startup. |
| Manual | Only when StartupComplete() runs or CompleteOnScenario finishes. |
| Injected | Leave stop timing to the official helper (first page handler). |
Set CliProfile.CompleteOnScenario to wait for a named flow instead of first page / first frame. CliProfile.Enabled = false skips automatic first-page / first-frame stop; manual StartupComplete() and scenarios still work.
The report
var report = MauiPerformance.GetReport();
Console.WriteLine(report.Format());
report.Metrics; // latest timing per name
report.AllMetrics; // full ring buffer
report.Memory.WorkingSetBytes;
report.Memory.AvailableBytes;
report.Memory.Pressure;Durations of one second or more print as 1.82 sec. Shorter work prints as 420 ms. MaxMetrics (default from MauiPerformanceDefaults) drops the oldest entries from the ring buffer.
When to use it
- How do I measure MAUI startup and page load time on a device?
- Trace API vs SQLite vs image load without Application Insights.
- How do I wrap maui profile / stop a startup .nettrace on first frame?
Do not use this package if you need a hosted APM, retain-path diagnosis (use a memory profiler), crash / ANR breadcrumbs (Diagnostics), or WeakReference liveness after navigation (LeakAnalyser).
Platforms and version
Version 1.0.7. Target frameworks: net10.0, net10.0-android (API 21+), net10.0-ios (iOS 15+). Mac Catalyst and Windows are not primary targets. net10.0 without an OS TFM is for shared code and tests — native APIs typically throw FeatureNotSupported.
| Android | iOS | net10.0 | |
|---|---|---|---|
| Named traces / report | Yes | Yes | Yes (tests) |
| Startup / page / navigation | Yes | Yes | Listener APIs |
| HTTP handler | Yes | Yes | Yes |
| Image aggregation | Yes | Yes | — |
| Next-frame render | Choreographer | CADisplayLink | Immediate |
| Memory probes | ActivityManager | os_proc_available_memory | GC + working set |
| maui profile / maui-perf | Device | Simulator only | — |
Library 1.0.7. Restore from nuget.org or the nuvyntralabs GitHub Packages feed. Plugin.Maui.Performance on nuget.org
dotnet tool maui-perf, packed at the same 1.0.7 version (PackAsTool, no snupkg). Plugin.Maui.Performance.Cli on nuget.org