Ambienti di runtime e compilazione in .NET MAUI

Le app .NET Multi-platform App UI (.NET MAUI) possono usare runtime e strategie di compilazione diverse a seconda della piattaforma di destinazione, della configurazione di compilazione e del modello di distribuzione. Questo articolo illustra i termini chiave e le strategie di runtime e compilazione che .NET MAUI usano in ogni piattaforma.

Runtime

Un runtime .NET è l'ambiente di esecuzione che gestisce la memoria, il sistema dei tipi, la raccolta dei rifiuti e l'esecuzione del codice dell'applicazione. .NET MAUI app usano uno dei runtime seguenti:

Mono

Mono è il runtime multipiattaforma .NET che ha storicamente alimentato app Xamarin e app .NET MAUI in Android, iOS e Mac Catalyst. Mono supporta sia la compilazione JIT (Just-In-Time) che la compilazione AOT (Ahead-of-Time). Mono è il runtime predefinito per le app MAUI .NET nelle piattaforme Mobili e Mac Catalyst.

Mono non è supportato per le app .NET MAUI destinate .NET 11+.

Coreclr

CoreCLR è .NET Common Language Runtime usato da .NET nelle piattaforme desktop e server. Alimenta ASP.NET Core, le applicazioni console e le applicazioni desktop per Windows. CoreCLR offre un compilatore JIT altamente ottimizzato, una compilazione a livelli e un set completo di diagnostica di runtime.

In .NET MAUI CoreCLR è il runtime usato in Windows. In .NET 10 CoreCLR è disponibile anche come opzione sperimentale per Android.

In .NET 11+, CoreCLR è il runtime predefinito per le app .NET MAUI in Windows, Android, iOS e Mac Catalyst. NativeAOT è un'alternativa facoltativa per la pubblicazione in tutte le piattaforme di destinazione di .NET 11. Il supporto nativeAOT è sperimentale in Android.

NativeAOT

Quando si pubblica con AOT nativo, l'app non viene eseguita nel runtime CoreCLR. Viene invece eseguito in un runtime NativeAOT minimo collegato in modo statico al file binario nativo. Questo runtime include un Garbage Collector e un sistema di tipi, ma nessun compilatore o interprete JIT. Tutto il codice viene compilato in codice del computer nativo in fase di compilazione.

Strategie di compilazione

Una strategia di compilazione determina il modo in cui il codice C# viene trasformato in codice computer che il processore può eseguire. .NET MAUI usa diverse strategie di compilazione, a seconda dello scenario di runtime e distribuzione.

Compilazione JIT (JUST-in-Time)

La compilazione JIT converte Microsoft Intermediate Language (MSIL) in codice del computer nativo in fase di esecuzione quando vengono chiamati i metodi per la prima volta. La compilazione JIT consente rapidi cicli di compilazione-distribuzione-debug e funzionalità come Modifica e continuazione.

  • Usato da: CoreCLR in Windows e sperimentalmente in Android; Mono nelle compilazioni di debug Android
  • Vantaggi: tempi di compilazione rapidi, diagnostica completa del runtime e supporto per la generazione di codice dinamico
  • Svantaggi: avvio più lento perché il codice viene compilato nel dispositivo in fase di esecuzione
  • Utilizzato da: CoreCLR su Windows e Android
  • Vantaggi: tempi di compilazione rapidi, diagnostica completa del runtime e supporto per la generazione di codice dinamico
  • Svantaggi: avvio più lento quando il codice non è già compilato da ReadyToRun

Le destinazioni dei dispositivi Apple CoreCLR non usano JIT perché le restrizioni della piattaforma Apple impediscono il codice generato dinamicamente. Usano invece l'interprete CoreCLR.

Compilazione Mono AOT (Ahead-of-Time - Compilazione Anticipata)

La compilazione Mono AOT precompila MSIL in codice nativo in fase di compilazione usando il compilatore AOT di Mono. Questa è la modalità di compilazione predefinita per le build della versione MAUI di .NET su iOS, Mac Catalyst e Android.

  • Usato da: il runtime Mono su iOS, Mac Catalyst e Android
  • Vantaggi: avvio più rapido rispetto a JIT e necessario per le piattaforme che limitano la generazione di codice dinamico
  • Svantaggi: dimensioni dell'app più grandi rispetto alle build solo JIT e supporto limitato per alcune funzionalità dinamiche

Mono AOT non è uguale a NativeAOT. Con Mono AOT, l'app include ancora il runtime mono e può facoltativamente usare l'interprete Mono per il codice che non è stato compilato con AOT. NativeAOT compila tutto il codice in anticipo e non include un interprete o JIT. Mono supporta anche una modalità AOT completa in cui non è disponibile alcun interprete o JIT. L'AOT completo viene usato per impostazione predefinita per le build di versione Mono in iOS e Mac Catalyst.

Compilazione NativeAOT (Native Ahead-of-Time)

NativeAOT compila l'intera app, le relative dipendenze e un runtime minimo in un file binario nativo in fase di compilazione. Non esiste alcun compilatore JIT o interprete in fase di esecuzione. NativeAOT esegue il taglio completo e l'analisi statica, che può produrre dimensioni più piccole delle app e tempi di avvio più rapidi, ma pone più restrizioni sui modelli di codice che è possibile usare.

  • Disponibile in: .NET MAUI app iOS e Mac Catalyst quando sono abilitate in modo esplicito per la pubblicazione
  • Vantaggi: piccole dimensioni dell'app, avvio rapido e un singolo file binario nativo
  • Svantaggi: tempi di compilazione più lunghi, nessuna generazione dinamica di codice né caricamento dinamico, e tutto il codice deve essere compatibile con il trimming e con AOT
  • Proprietà MSBuild: <PublishAot>true</PublishAot>
  • Disponibile in: tutte le piattaforme di destinazione .NET MAUI, se esplicitamente abilitate per la pubblicazione. Il supporto nativeAOT è sperimentale in Android.
  • Vantaggi: piccole dimensioni dell'app, avvio rapido e un singolo file binario nativo
  • Svantaggi: tempi di compilazione più lunghi, nessuna generazione dinamica di codice né caricamento dinamico, e tutto il codice deve essere compatibile con il trimming e con l'AOT
  • Proprietà MSBuild: <PublishAot>true</PublishAot>

NativeAOT è un modello di distribuzione di sola pubblicazione. Usare dotnet publish per produrre e convalidare un'app NativeAOT. Una compilazione di debug normale non usa NativeAOT.

Per altre informazioni, vedere Distribuzione AOT nativa in iOS e Mac Catalyst e distribuzione AOT nativa.

ReadyToRun (R2R)

ReadyToRun è una forma di compilazione anticipata per CoreCLR che precompila gli assembly in un formato contenente sia il codice MSIL originale che una rappresentazione di codice nativo. All'avvio, il runtime può usare il codice nativo precompilato anziché compilare metodi da MSIL. Nelle piattaforme in cui è disponibile JIT, il runtime può comunque compilare metodi JIT che non sono precompilati e ottimizzano i metodi usati di frequente in fase di esecuzione.

  • Usato da: CoreCLR in Windows e build CoreCLR Android che optano per il runtime sperimentale
  • Vantaggi: tempo di avvio migliorato mantenendo la compatibilità JIT
  • Svantaggi: dimensioni di assembly maggiori perché gli assembly contengono sia codice MSIL che codice nativo
  • Proprietà MSBuild: <PublishReadyToRun>true</PublishReadyToRun>

ReadyToRun è abilitato per impostazione predefinita per le app .NET MAUI in Windows in Release modalità e per le app Android che usano CoreCLR (sperimentale) in Release modalità. Le immagini R2R vengono compresse all'interno dei .dll file.

  • Usato da: CoreCLR in Windows, Android, iOS e Mac Catalyst
  • Vantaggi: tempo di avvio migliorato mantenendo il comportamento di compilazione di runtime appropriato
  • Svantaggi: dimensioni maggiori dell'applicazione e dei download

Per le app Android, ReadyToRun è abilitato per impostazione predefinita nella Release configurazione. Il valore predefinito è ReadyToRun parziale composito. Full ReadyToRun può essere abilitato con MauiEnableFullReadyToRun=true. Può migliorare le prestazioni di avvio o di runtime, ma aumenta le dimensioni del pacchetto, in modo da misurare il risultato per l'app.

Per le app iOS e Mac Catalyst, nelle compilazioni Release viene usato ReadyToRun composito parziale e nelle compilazioni Debug viene usato ReadyToRun composito completo. L'interprete CoreCLR è sempre abilitato ed esegue codice non precompilato perché queste piattaforme non consentono la compilazione JIT.

Composito R2R compila gli assembly insieme, abilitando le ottimizzazioni tra assembly. Parziale R2R precompila i metodi selezionati lasciando i metodi rimanenti per la compilazione in fase di esecuzione, mentre r2R completa precompila tutti i metodi.

Per altre informazioni, vedere Compilazione ReadyToRun.

Ottimizzazione guidata dal profilo (PGO)

L'ottimizzazione PGO (Profile-Guided Optimization) usa i dati di profilatura per guidare il compilatore verso la produzione di codice più efficiente. Nel contesto di .NET MAUI, esistono due forme di PGO:

  • PGO statico con profili MIBC: .NET MAUI viene fornito con file di profilo .mibc contenenti dati sui metodi chiamati frequentemente durante il tipico avvio e utilizzo dell'app. Quando la compilazione ReadyToRun è abilitata, il compilatore R2R usa questi profili per decidere quali metodi precompilare.
  • PGO dinamico: in CoreCLR, il compilatore JIT può raccogliere i dati del profilo in fase di esecuzione e usarli quando i metodi vengono ricompilati con altre ottimizzazioni.

Per altre informazioni, vedere Ottimizzazione guidata dal profilo e Compilazione ReadyToRun.

Interprete Mono

L'interprete Mono consente a un'app di interpretare MSIL in fase di esecuzione senza generare codice nativo in modo dinamico. Viene usato insieme a Mono AOT su piattaforme che non consentono la compilazione JIT, ad esempio i dispositivi iOS.

L'interprete Mono abilita anche il ricaricamento rapido .NET per le app in esecuzione nel runtime mono. UseInterpreter è impostato su true per impostazione predefinita in modalità Debug su Android, iOS e Mac Catalyst.

  • Usato da: runtime Mono
  • Vantaggi: supporta funzionalità dinamiche che AOT non può gestire, abilita .NET Ricaricamento rapido e può ridurre le dimensioni delle app
  • Svantaggi: il codice interpretato viene eseguito più lentamente del codice compilato
  • Proprietà MSBuild: <UseInterpreter>true</UseInterpreter>

Per altre informazioni, vedere Interprete Mono in iOS e Mac Catalyst.

interprete CoreCLR

CoreCLR include un interprete su iOS e Mac Catalyst. L'interprete è sempre abilitato ed esegue codice non precompilato perché queste piattaforme non consentono la compilazione JIT. Questo comportamento fa parte del runtime e non richiede una proprietà MSBuild dell'interprete.

Che cosa usa .NET MAUI per impostazione predefinita

La tabella seguente riepiloga la strategia di runtime e compilazione usata da .NET MAUI in ogni piattaforma, in base alla configurazione di compilazione:

Piattaforma Debug Rilascio
Android Mono + JIT + Interpreter Mono + Mono AOT
iOS Mono + JIT (x64) / Mono + AOT + interprete (ARM64) Mono + Mono AOT
Mac Catalyst Mono + JIT (x64) / Mono + AOT + interprete (ARM64) Mono + Mono AOT
Windows CoreCLR + JIT CoreCLR + JIT + ReadyToRun

Annotazioni

Quando si acconsente esplicitamente a CoreCLR in Android o iOS impostando <UseMonoRuntime>false</UseMonoRuntime> nel file di progetto, le compilazioni usano ReadyToRun per impostazione predefinita. Su iOS e Mac Catalyst con CoreCLR, Composite ReadyToRun è abilitato sia per le build di debug sia per le build di rilascio.

Suggerimento

È possibile acconsentire esplicitamente a NativeAOT in iOS e Mac Catalyst impostando <PublishAot>true</PublishAot> nel file di progetto. NativeAOT ha effetto solo durante la pubblicazione.

La tabella seguente riepiloga la strategia di runtime e compilazione usata da .NET MAUI in ogni piattaforma:

Piattaforma Debug Rilascio
Android CoreCLR + JIT CoreCLR + composite partial ReadyToRun + JIT
iOS CoreCLR + ReadyToRun parziale composito + interprete CoreCLR + ReadyToRun composito completo + interprete
Mac Catalyst CoreCLR + composite partial ReadyToRun + interprete CoreCLR + ReadyToRun completo composito + interprete
Windows CoreCLR + JIT CoreCLR + JIT + ReadyToRun

Importante

Non impostare UseMonoRuntime su true quando la destinazione è .NET 11+. Mono non è supportato e l'impostazione di questa proprietà genera un errore di compilazione.

Suggerimento

NativeAOT è abilitato con <PublishAot>true</PublishAot> e diventa effettivo quando si usa dotnet publish. NativeAOT non dispone di un JIT né di un interprete e richiede un trimming completo.

Confronto

La tabella seguente confronta le strategie di compilazione disponibili per le app MAUI .NET:

Strategia JIT Mono AOT ReadyToRun NativeAOT
Tempo di compilazione In fase di esecuzione In fase di compilazione In fase di compilazione In fase di compilazione
Velocità di avvio Più lento Veloce Veloce Il più veloce
Velocità di stato costante Più veloce (JIT ottimizzato) Bene Più veloce (ricompilazione incrementale a livelli) Bene
Dimensioni dell'app Più piccolo Maggiore Maggiore Più piccolo
Codice dinamico Supporto completo Limitato Supporto completo Non supportato
Taglio obbligatorio No Parziale (impostazione predefinita) No Completo
Diagnostica Completo Limitato Completo Limitato

Prestazioni tipiche

I benchmark seguenti provengono da un'app dotnet new maui e dipendono dall'hardware. Illustrano le differenze relative tra i runtime e non sono garanzie assolute.

Tempo di avvio (millisecondi):

Runtime Android iOS macOS
Mono (impostazione predefinita) ~642 ~275 ~311
NativeAOT ~274 ~130 ~255

Dimensioni dell'app in iOS (MB):

Modello di distribuzione Dimensione
Mono (impostazione predefinita) ~14.3
Mono (ritaglio completo) ~11.5
NativeAOT ~5.4

La tabella seguente confronta le strategie di compilazione disponibili per le app .NET MAUI destinate a .NET 11:

Strategia CoreCLR JIT interprete CoreCLR ReadyToRun NativeAOT
Tempo di compilazione In fase di esecuzione In fase di esecuzione In fase di compilazione In fase di compilazione
Velocità di avvio Più lento Più lento Veloce Il più veloce
Dimensioni dell'app Più piccolo Più piccolo Maggiore Più piccolo
Codice dinamico Supporto completo Supporto completo Supporto completo (tramite JIT o interprete) Non supportato
Diagnostica Completo Completo Limitato Limitato

CoreCLR in Android e iOS

A partire da .NET 10, è possibile acconsentire esplicitamente all'esecuzione di un'app Android in CoreCLR invece di Mono.

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
    <UseMonoRuntime>false</UseMonoRuntime>
</PropertyGroup>

In .NET 10 CoreCLR in Android è una funzionalità sperimentale e non è destinata all'uso in produzione.

In .NET 11+, CoreCLR è il runtime predefinito per le app Android, iOS e Mac Catalyst. NativeAOT è un'alternativa di consenso esplicito che non usa CoreCLR ed è sperimentale in Android. Non impostare UseMonoRuntime=true; Mono non è supportato per .NET 11+ e l'impostazione della proprietà genera un errore di compilazione.

All'interno di un APK Android

Il contenuto di un APK varia a seconda della strategia di runtime e compilazione usata dall'app:

Mono:

  • classes.dex — Codice Java/Kotlin
  • lib/arm64-v8a/libmonosgen-2.0.so — Mono Runtime
  • lib/arm64-v8a/libmonodroid.so — Codice di integrazione per l'avvio del runtime specifico di Android
  • lib/arm64-v8a/libassemblies.arm64-v8a.blob.so — Assembly MSIL compressi e impacchettati
  • lib/arm64-v8a/libaot-*.dll.so — Immagini native mono AOT nelle build di rilascio

CoreCLR:

  • classes.dex — Codice Java/Kotlin
  • lib/arm64-v8a/libcoreclr.so — Runtime CoreCLR
  • lib/arm64-v8a/libclrjit.so — Compilatore JIT CoreCLR
  • lib/arm64-v8a/libmonodroid.so — codice di avvio del runtime specifico per Android
  • lib/arm64-v8a/libassemblies.arm64-v8a.so — MSIL impacchettato con immagini ReadyToRun

Il contenuto di un APK Android varia a seconda che l'app usi CoreCLR o NativeAOT:

CoreCLR:

  • classes.dex — Codice Java/Kotlin
  • lib/arm64-v8a/libcoreclr.so — Runtime CoreCLR
  • lib/arm64-v8a/libclrjit.so — Compilatore JIT CoreCLR
  • lib/arm64-v8a/libmonodroid.so — codice di integrazione per l'avvio dell'ambiente di runtime specifico di Android
  • lib/arm64-v8a/libassembly-store.so — MSIL compresso con immagini ReadyToRun

NativeAOT in Android (sperimentale):

  • classes.dex — Codice Java/Kotlin
  • lib/arm64-v8a/libhellomaui.so — Libreria nativa contenente il runtime, il codice gestito e il codice di avvio

Taglio

Trimming è un passaggio di compilazione che rimuove il codice inutilizzato dall'app per ridurne le dimensioni. .NET MAUI usa il trimmer ILLink, che analizza il codice e rimuove tipi, metodi e campi a cui non viene fatto riferimento staticamente.

Per le build di rilascio CoreCLR, .NET MAUI usa TrimMode=partial per impostazione predefinita, che riduce gli assembly del framework, ma non il codice dell’app né i riferimenti NuGet. Per usare il trimming completo, impostare TrimMode su full:

<PropertyGroup>
    <TrimMode>full</TrimMode>
</PropertyGroup>

NativeAOT esegue automaticamente il taglio completo. Non impostare TrimMode per selezionare una modalità di taglio diversa quando si usa NativeAOT.

Per altre informazioni, vedere Ottimizzare un'app .NET MAUI.

Informazioni di riferimento sulle proprietà di MSBuild

La tabella seguente riepiloga le proprietà di MSBuild che controllano il comportamento di runtime e compilazione:

Proprietà Descrizione Impostazione predefinita
PublishAot Abilitare la compilazione NativeAOT. false
PublishReadyToRun Abilitare la precompilazione ReadyToRun per CoreCLR. true (release di Windows e build CoreCLR correlate)
PublishTrimmed Abilitare la riduzione ILLink. true (Versione)
TrimMode Impostare l'aggressività di taglio (partial o full). partial
UseInterpreter Abilitare l'interprete Mono. true (Debugging Catalyst per iOS/Mac)
UseMonoRuntime Usare Mono anziché CoreCLR. true nelle destinazioni Mono supportate

La tabella seguente riepiloga le proprietà di MSBuild rilevanti per il comportamento di runtime e compilazione di .NET 11:

Proprietà Descrizione Impostazione predefinita
PublishAot Abilitare la compilazione NativeAOT durante dotnet publish. false
MauiEnableFullReadyToRun Abilitare ReadyToRun completo per le app Android CoreCLR. false
TrimMode Impostare l'aggressività di taglio per le compilazioni non NativeAOT. partial

Vedere anche