Compilazione ReadyToRun

I tempi di avvio e la latenza dell'applicazione .NET possono essere migliorati compilando gli assembly dell'applicazione come formato ReadyToRun (R2R). R2R è un formato di compilazione AOT (Ahead-of-time).

I file binari R2R migliorano le prestazioni di avvio riducendo la quantità di lavoro che deve eseguire il compilatore JIT (Just-in-time) durante il caricamento dell'applicazione. I file binari contengono codice nativo simile a ciò che viene generato dal compilatore JIT. I file binari R2R, tuttavia, sono più grandi poiché contengono sia il codice in linguaggio intermedio, che è comunque necessario per alcuni scenari, sia la versione nativa dello stesso codice. R2R è disponibile solo quando si pubblica un'app destinata ad ambienti di runtime (RID) specifici, ad esempio Linux x64 o Windows x64.

Per compilare il progetto come ReadyToRun, l'applicazione deve essere pubblicata con la proprietà PublishReadyToRun impostata su true.

Esistono due modi per pubblicare l'app come ReadyToRun:

  1. Specificare il flag PublishReadyToRun direttamente nel comando dotnet publish. Per informazioni dettagliate, vedere dotnet publish.

    dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true
    
  2. Specificare la proprietà nel progetto.

    • Aggiungere l'impostazione <PublishReadyToRun> al progetto.
    <PropertyGroup>
      <PublishReadyToRun>true</PublishReadyToRun>
    </PropertyGroup>
    
    • Pubblicare l'applicazione senza parametri speciali.
    dotnet publish -c Release -r win-x64
    

Impatto dell'uso della funzionalità ReadyToRun

La compilazione in anticipo ha un impatto complesso sulle prestazioni dell'applicazione, che può essere difficile da prevedere. In generale, le dimensioni di un assembly aumentano di due o tre volte. Questo aumento delle dimensioni fisiche del file può ridurre le prestazioni del caricamento dell'assembly dal disco e aumentare il working set del processo. Tuttavia, in compenso, il numero di metodi compilati in fase di esecuzione di solito si riduce sostanzialmente. Il risultato è che la maggior parte delle applicazioni con grandi quantità di codice offre grandi vantaggi in termini di prestazioni dall'abilitazione di ReadyToRun. Le applicazioni con piccole quantità di codice probabilmente non avranno un miglioramento significativo dall'abilitazione di ReadyToRun, perché le librerie di runtime .NET sono già state precompilate con ReadyToRun.

Il miglioramento dell’avvio descritto in questo articolo si applica non solo all'avvio dell'applicazione, ma anche al primo uso di qualsiasi codice nell'applicazione. Ad esempio, ReadyToRun può essere usato per ridurre la latenza di risposta del primo uso dell'API Web in un'applicazione ASP.NET.

Interazione con la compilazione a livelli

Il codice generato in anticipo non è altamente ottimizzato come il codice prodotto da JIT. Per risolvere questo problema, la compilazione a livelli sostituirà i metodi ReadyToRun comunemente usati con metodi generati da JIT.

Come viene scelto il set di assembly precompilati?

L'SDK precompila gli assembly distribuiti con l'applicazione. Per le applicazioni autonome, questo set di assembly includerà il framework. I file binari C++/CLI non sono idonei per la compilazione ReadyToRun.

Per escludere assembly specifici dall'elaborazione ReadyToRun, usare l'elenco <PublishReadyToRunExclude>.

<ItemGroup>
  <PublishReadyToRunExclude Include="Contoso.Example.dll" />
</ItemGroup>

Come viene scelto il set di metodi da precompilare?

Il compilatore tenterà di precompilare tutti i metodi che può. Tuttavia, per vari motivi, non è previsto che l'uso della funzionalità ReadyToRun impedisca l'esecuzione di JIT. Tali motivi possono includere, a titolo esemplificativo e non esaustivo:

  • Uso di tipi generici definiti in assembly separati.
  • Interoperabilità con codice nativo.
  • Uso di intrinseci hardware che il compilatore non può dimostrare di utilizzare in modo sicuro sul computer di destinazione.
  • Alcuni schemi IL insoliti.
  • Creazione di metodi dinamici tramite reflection o LINQ.

Generazione di simboli da usare con i profiler

Quando si compila un'applicazione con ReadyToRun, i profiler possono richiedere simboli per esaminare i file ReadyToRun generati. Per abilitare la generazione di simboli, specificare la proprietà <PublishReadyToRunEmitSymbols>.

<PropertyGroup>
  <PublishReadyToRunEmitSymbols>true</PublishReadyToRunEmitSymbols>
</PropertyGroup>

Questi simboli verranno inseriti nella directory di pubblicazione e per Windows avrà un'estensione di file .ni.pdb e per Linux avrà un'estensione di file .r2rmap. Questi file non vengono in genere ridistribuiti ai clienti finali, ma in genere vengono archiviati in un server di simboli. In generale, questi simboli sono utili per il debug di problemi di prestazioni correlati all'avvio delle applicazioni, perché la compilazione a livelli sostituirà il codice generato ReadyToRun con codice generato dinamicamente. Tuttavia, se si tenta di profilare un'applicazione che disabilita la compilazione a livelli i simboli saranno utili.

ReadyToRun composito

La compilazione ReadyToRun normale produce file binari che possono essere gestiti e modificati singolarmente. A partire da .NET 6, è stato aggiunto il supporto per la compilazione di ReadyToRun compositi. Il ReadyToRun composito compila un set di assembly che devono essere distribuiti insieme. Questo ha il vantaggio che il compilatore è in grado di eseguire ottimizzazioni migliori e riduce il set di metodi che non possono essere compilati tramite il processo ReadyToRun. Tuttavia, come compromesso, la velocità di compilazione è notevolmente diminuita e le dimensioni complessive del file dell'applicazione sono notevolmente aumentate. A causa di questi compromessi, l'uso del ReadyToRun composito è consigliato solo per le applicazioni che disabilitano la compilazione a livelli o le applicazioni in esecuzione in Linux che cercano il tempo di avvio migliore con la distribuzione autonoma. Per abilitare la compilazione del ReadyToRun composito, specificare la proprietà <PublishReadyToRunComposite>.

<PropertyGroup>
  <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
</PropertyGroup>

Nota

In .NET 6 il ReadyToRun composito è supportato solo per la distribuzione autonoma.

Limitazioni per ambienti con più piattaforme o architetture

Per alcune piattaforme SDK, il compilatore ReadyToRun è in grado di eseguire la compilazione incrociata per altre piattaforme di destinazione.

Le destinazioni di compilazione supportate sono descritte nella tabella seguente quando la destinazione è .NET 6 e versioni successive.

Piattaforma SDK Piattaforme di destinazione supportate
Windows x64 Windows (X86, X64, Arm64), Linux (X64, Arm32, Arm64), macOS (X64, Arm64)
Windows X86 Windows (X86), Linux (Arm32)
Linux X64 Linux (X64, Arm32, Arm64), macOS (X64, Arm64)
Linux Arm32 Linux Arm32
Linux Arm64 Linux (X64, Arm32, Arm64), macOS (X64, Arm64)
macOS X64 Linux (X64, Arm32, Arm64), macOS (X64, Arm64)
macOS Arm64 Linux (X64, Arm32, Arm64), macOS (X64, Arm64)

Le destinazioni di compilazione supportate sono descritte nella tabella seguente quando la destinazione è .NET 5 e versioni successive.

Piattaforma SDK Piattaforme di destinazione supportate
Windows x64 Windows X86, Windows X64, Windows Arm64
Windows X86 Windows X86, Windows Arm32
Linux X64 Linux X86, Linux X64, Linux Arm32, Linux Arm64
Linux Arm32 Linux Arm32
Linux Arm64 Linux Arm64
macOS X64 macOS X64