Uno Platform AOT Compilation: Complete Cross-Platform Native Performance Guide
This post presents a full-scope study of AOT (ahead-of-time) compilation support in Uno Platform, covering execution modes, platform toolchains, configuration steps, optimization strategies, and compatibility constraints for iOS, Android, WebAssembly, and desktop targets.
Key points
- Four runtime execution modes: Interpreter (default, ~30x slower than compiled WASM), Mixed (
InterpreterAndAOT, ~50% smaller than full AOT, recommended for WASM production), Full AOT (mandatory on iOS), and Jiterpreter (.NET 9+, runtime JIT within the interpreter, up to 10x speedup when combined with Profiled AOT). - Platform toolchain matrix: iOS uses Mono Full AOT (required by Apple's no-dynamic-code policy / W^X memory protection); Android uses Mono AOT with optional LLVM backend (
-p:EnableLLVM=true, ~20-40% perf gain) and Profile-Guided AOT; WebAssembly uses Mono AOT via Emscripten; Skia Desktop and WinUI 3 use .NET Native AOT (ILC + RyuJIT). - .NET 9 milestones: Jiterpreter, incremental AOT builds, WebCIL packaging, and automatic Emscripten toolchain management (no manual EMSDK install; ARM64 host support on Windows/macOS/Linux).
- Version highlights: Uno 4.7 introduced Native AOT for Skia+GTK; Uno 5.6 delivered up to 10x WASM AOT performance; Uno 6.0 replaced WPF with a Win32 backend, cutting Windows blank-app size by 74%, iOS by 21%, and improving AOT build times by 35%. iOS startup on iPhone 13 dropped from 1.5s to 0.49s with the unified Skia engine.
- Full AOT is forced by the platform; no
PublishAotneeded. Release config relies onRuntimeIdentifier=ios-arm64,PublishTrimmed, andUnoXamlTrimming(Uno 6.0+). - Trimming modes:
None,SdkOnly(default),Full(aggressive; mitigate with[DynamicDependency]and LinkerConfig.xml). - Not available on iOS:
System.Reflection.Emit,DynamicMethod,Assembly.Load(byte[]), Roslyn scripting;RegexOptions.Compiledis ignored. Use source generators,[JsonSerializable]-basedSystem.Text.Jsonsource-gen serialization, and cached delegates instead. - GC tuning via
MtouchEnableSGenConc,MtouchNurserySize, orMONO_GC_PARAMSenvironment variables. - AOT must be explicitly enabled:
RunAOTCompilation=true,EnableLLVM=true,AndroidEnableMarshalMethods=true(.NET 8+, reduces JNI overhead 15-25%),PublishTrimmed=true,AndroidEnableProfiledAot=true. - Startup Tracing workflow: add
Mono.AotProfiler.Android, rundotnet build -t:BuildAndStartAotProfilingwith profiling disabled AOT, exercise the app 30-60s, thenFinishAotProfilingto producecustom.aprof; integrate via<AndroidAotProfile Include="Android/custom.aprof" />. - Trade-off matrix (relative to interpreter baseline): PG-AOT ≈120-140% size, ~90% perf (production default); Full AOT + LLVM ≈180-220% size, 100% perf, 2-3x build time.
- Google Play requires AAB format (
-p:AndroidPackageFormat=aab) and arm64-v8a; multi-ABI builds scale AOT time linearly. String resource trimming viaAndroidLinkResourcescan cut 10-30% of resource size. - Production setup:
<WasmShellMonoRuntimeExecutionMode>InterpreterAndAOT</WasmShellMonoRuntimeExecutionMode>— active only ondotnet publish/ Release builds. - Profile-Guided AOT: set
WasmShellGenerateAOTProfile=true, run typical scenarios without debugger, pressAlt+Shift+Pto download the.aotprofile, then include it viaWasmShellEnableAotProfile(auto-detected under Platforms/WebAssembly/ with Uno.Sdk). - Jiterpreter (.NET 9+):
<WasmShellEnableJiterpreter>true</WasmShellEnableJiterpreter>; tune viaWasmShellRuntimeOptions(--jiterpreter-estimate-heat,--jiterpreter-stats-enable); inspect stats withINTERNAL.jiterpreter_dump_stats()in the browser console. Recommended: enable Mixed AOT and Jiterpreter together — they are complementary. - Skia desktop targets (GTK/Linux/macOS/Windows) support .NET Native AOT for single-file self-contained deployment (supported since Uno 4.7). WPF does not support Native AOT — migrate to WinUI 3 or GTK. WinUI 3 with the Skia+Win32 path is the recommended Windows configuration on Uno 6.0+.
iOS
Android
WebAssembly
Desktop
General compatibility advice
Verify third-party NuGet packages for trim-friendliness (IsTrimmable metadata), reflection/dynamic-proxy usage, native ARM64/P/Invoke availability, and known AOT issues before adoption; validate with trimmer warnings and on-device testing.