Skip to content
Nuvyntra Labs

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.

SignalHow
Startup timeProcess start (or Start()) to the first content page. Recorded as App Startup.
Page startupPage appearing until Loaded. Named from the page title (Home → Home Page).
Navigation timePrevious page disappearing until the next appearing (Home Page → Customer Page).
API latencyPerformanceDelegatingHandler on HttpClient. Path /customer becomes Customer API.
Image loadingImages on each page, aggregated as Image Loading.
UI renderingNext display frame after the page loads (UI Render). Android Choreographer, iOS CADisplayLink.
MemoryWorking set / managed heap plus free RAM on Android (ActivityManager) and iOS (os_proc_available_memory).
DatabaseNot 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.

APIWhat it does
MauiProfile.IsSessiontrue 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.CompleteOnWhen the startup trace stops
FirstFrame (default)After the first display frame following the first page load.
FirstPageWhen the first content page appears — the same moment as App Startup.
ManualOnly when StartupComplete() runs or CompleteOnScenario finishes.
InjectedLeave 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.

AndroidiOSnet10.0
Named traces / reportYesYesYes (tests)
Startup / page / navigationYesYesListener APIs
HTTP handlerYesYesYes
Image aggregationYesYes
Next-frame renderChoreographerCADisplayLinkImmediate
Memory probesActivityManageros_proc_available_memoryGC + working set
maui profile / maui-perfDeviceSimulator 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

Source and sample on GitHub

Discussion

Comment on Plugin.Maui.Performance. The thread lives on this component's GitHub repository (nuvyntralabs/Plugin.Maui.Performance). Sign in with GitHub — Giscus uses Discussions, Utterances uses Issues.