ReadyToRun-Kompilierung

Die Start- und Wartezeiten für .NET-Anwendungen können verbessert werden, indem Sie Ihre Anwendungsassemblys im R2R-Format (ReadyToRun) kompilieren. R2R ist eine Form der AOT-Kompilierung (Ahead-Of-Time).

R2R-Binärdateien verbessern die Startleistung, indem sie den Arbeitsaufwand des JIT-Compilers (Just-in-time) reduzieren, der anfällt, wenn Ihre Anwendung geladen wird. Die Binärdateien enthalten ähnlichen nativen Code im Vergleich zu dem, was der JIT-Compiler produzieren würde. R2R-Binärdateien sind jedoch größer, da sie sowohl IL-Code (Intermediate Language, Zwischensprache), der für einige Szenarios noch benötigt wird, als auch die native Version des gleichen Codes enthalten. R2R ist nur bei Veröffentlichung einer App verfügbar, die auf bestimmte Laufzeitumgebungen (RID) wie Linux x64 oder Windows x64 ausgerichtet ist.

Damit Ihr Projekt als ReadyToRun kompiliert werden kann, muss die PublishReadyToRun-Eigenschaft bei der Anwendungsveröffentlichung auf true festgelegt sein.

Es gibt zwei Möglichkeiten, Ihre App als ReadyToRun zu veröffentlichen:

  1. Geben Sie das Flag „PublishReadyToRun“ direkt im Befehl „dotnet publish“ an. Weitere Informationen finden Sie unter dotnet publish.

    dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true
    
  2. Geben Sie die Eigenschaft im Projekt an.

    • Fügen Sie Ihrem Projekt die Einstellung <PublishReadyToRun> hinzu.
    <PropertyGroup>
      <PublishReadyToRun>true</PublishReadyToRun>
    </PropertyGroup>
    
    • Veröffentlichen Sie die Anwendung ohne spezielle Parameter.
    dotnet publish -c Release -r win-x64
    

Auswirkungen der Verwendung des ReadyToRun-Features

Die Ahead-of-Time-Kompilierung hat komplexe Auswirkungen auf die Anwendungsleistung, die sich möglicherweise nur schwer vorhersagen lassen. Im allgemeinen wächst die Größe einer Assembly auf das Zwei- bis Dreifache an. Durch diese Zunahme der physischen Dateigröße kann sich die Leistung beim Laden der Assembly von einem Datenträger verringern und der Arbeitssatz des Prozesses erhöhen. Auf der anderen Seite wird die Anzahl von Methoden, die zur Laufzeit kompiliert werden, in der Regel erheblich reduziert. Unterm Strich können die meisten Anwendungen mit umfangreichem Code durch die Aktivierung von ReadyToRun von großen Leistungsvorteilen profitieren. Bei Anwendungen, die nur eine geringe Codemenge umfassen, können durch die Aktivierung von ReadyToRun wahrscheinlich keine wesentliche Verbesserung erzielen, weil die .NET-Runtimebibliotheken bereits mit ReadyToRun vorkompiliert wurden.

Die hier beschriebene Startverbesserung gilt nicht nur für den Anwendungsstart, sondern auch für die erste Verwendung von Code in der Anwendung. So kann beispielsweise die Antwortlatenz bei der ersten Verwendung der Web-API in einer ASP.NET-Anwendung mithilfe von ReadyToRun reduziert werden.

Interaktion mit mehrstufiger Kompilierung

Der durch die Ahead-of-Time-Kompilierung generierte Code ist nicht so stark optimiert wie Code, der durch die JIT-Kompilierung erzeugt wird. Um dieses Problem zu beheben, werden bei der mehrstufigen Kompilierung häufig verwendete ReadyToRun-Methoden durch JIT-generierte Methoden ersetzt.

Wie wird der Satz vorkompilierter Assemblys ausgewählt?

Das SDK führt eine Vorkompilierung der Assemblys aus, die mit der Anwendung verteilt werden. Bei eigenständigen Anwendungen schließt dieser Satz von Assemblys das Framework mit ein. C++-/CLI-Binärdateien sind für die ReadyToRun-Kompilierung nicht geeignet.

Um bestimmte Assemblys von der ReadyToRun-Verarbeitung auszuschließen, verwenden Sie die <PublishReadyToRunExclude>-Liste.

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

Wie wird der Satz von Methoden für die Vorkompilierung ausgewählt?

Der Compiler versucht, so viele Methoden wie möglich vorab zu kompilieren. Aus verschiedenen Gründen ist jedoch nicht zu erwarten, dass die Verwendung des ReadyToRun-Features eine JIT-Ausführung verhindert. Hierzu gehören:

  • Verwendung generischer Typen, die in separaten Assemblys definiert sind
  • Interop mit nativem Code
  • Verwendung intrinsischer Hardwarefunktionen, deren Verwendung auf einem Zielcomputer vom Compiler nicht als sicher eingestuft werden kann
  • Bestimmte ungewöhnliche IL-Muster
  • Dynamische Methodenerstellung über Reflexion oder LINQ

Symbolgenerierung für die Verwendung mit Profilern

Beim Kompilieren einer Anwendung mit ReadyToRun benötigen Profiler möglicherweise Symbole zum Untersuchen der generierten ReadyToRun-Dateien. Um die Symbolgenerierung zu aktivieren, geben Sie die <PublishReadyToRunEmitSymbols>-Eigenschaft an.

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

Diese Symbole werden im Veröffentlichungsverzeichnis platziert. Für Windows wird die Dateierweiterung „.ni.pdb“, für Linux die Dateierweiterung „.r2rmap“verwendet. Diese Dateien werden im Allgemeinen nicht an Endkunden verteilt, sondern in der Regel auf einem Symbolserver gespeichert. Im Allgemeinen sind diese Symbole nützlich für das Debuggen von Leistungsproblemen im Zusammenhang mit dem Start von Anwendungen, da die mehrstufige Kompilierung den generierten ReadyToRun-Code durch dynamisch generierten Code ersetzt. Wenn Sie jedoch versuchen, ein Profil für eine Anwendung zu erstellen, die mehrstufige Kompilierung deaktiviert, sind die Symbole nützlich.

Zusammengesetzte ReadyToRun-Kompilierung

Die normale ReadyToRun-Kompilierung generiert Binärdateien, die einzeln verwaltet und bearbeitet werden können. Ab .NET 6 wurde Unterstützung für zusammengesetzte ReadyToRun-Kompilierung hinzugefügt. Die zusammengesetzte ReadyToRun-Kompilierung kompiliert einen Satz von Assemblys, die zusammen verteilt werden müssen. Dies hat den Vorteil, dass der Compiler bessere Optimierungen durchführen kann und den Satz von Methoden reduziert, die nicht über den ReadyToRun-Prozess kompiliert werden können. Im Gegenzug verringert sich jedoch die Kompiliergeschwindigkeit erheblich, und die Gesamtgröße der Anwendungsdateien nimmt deutlich zu. Aufgrund dieser Kompromisse wird die Verwendung der zusammengesetzten ReadyToRun-Kompilierung nur für Anwendungen empfohlen, die die mehrstufige Kompilierung deaktivieren, oder für Anwendungen, die unter Linux ausgeführt werden und die beste Startzeit mit eigenständiger Bereitstellung anstreben. Um zusammengesetzte ReadyToRun-Kompilierung zu aktivieren, geben Sie die <PublishReadyToRunComposite>-Eigenschaft an.

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

Hinweis

In .NET 6 wird die zusammengesetzte ReadyToRun-Kompilierung nur für eine eigenständige Bereitstellung unterstützt.

Plattformübergreifende bzw. architekturbezogene Einschränkungen

Für einige SDK-Plattformen kann der ReadyToRun-Compiler eine Crosskompilierung für andere Zielplattformen durchführen.

Unterstützte Kompilierungsziele für .NET 6 und höhere Versionen werden in der folgenden Tabelle beschrieben.

SDK-Plattform Unterstützte Plattformen
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)

Unterstützte Kompilierungsziele für .NET 5 oder früher werden in der folgenden Tabelle beschrieben.

SDK-Plattform Unterstützte Plattformen
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