Neuerungen in der .NET 11 Laufzeit

In diesem Artikel werden neue Features in der .NET Laufzeit für .NET 11 beschrieben. Es wurde zuletzt für Preview 3 aktualisiert.

Aktualisierte Mindesthardwareanforderungen

Die Mindesthardwareanforderungen für .NET 11 wurden aktualisiert, um modernere Anweisungssätze für x86/x64- und Arm64-Architekturen zu erfordern. Darüber hinaus wurden die ReadyToRun (R2R)-Kompilierungsziele aktualisiert, um neuere Hardwarefunktionen zu nutzen.

Arm64-Anforderungen

Für Apple gibt es keine Änderung an der Mindesthardware oder dem ReadyToRun-Ziel. Die Apple M1-Chips sind ungefähr gleichwertig zu armv8.5-a und bieten Unterstützung für mindestens die Anweisungssätze AdvSimd (NEON), CRC, DOTPROD, LSE, RCPC, RCPC2 und RDMA.

Für Linux gibt es keine Änderung an der Mindesthardware. .NET unterstützt weiterhin Geräte wie Raspberry Pi, die möglicherweise nur Unterstützung für den AdvSimd Anweisungssatz bieten. Das ReadyToRun-Ziel wurde aktualisiert und enthält nun den LSE-Befehlssatz, was zu einem zusätzlichen Jitting-Overhead führen kann, wenn Sie eine Anwendung starten.

Für Windows wird der Basisplan so aktualisiert, dass der LSE-Anweisungssatz erforderlich ist. Dies wird von Windows 11 und von allen Arm64-CPUs, die offiziell von Windows 10 unterstützt werden benötigt. Darüber hinaus steht sie im Einklang mit den Anforderungen der Arm SBSA (Server Base System Architecture). Das ReadyToRun-Ziel wurde zu armv8.2-a + RCPC aktualisiert, das Unterstützung für mindestens AdvSimd, CRC, LSE, RCPC und RDMA bietet und die Mehrheit der offiziell unterstützten Hardware abdeckt.

OS Vorheriges JIT/AOT-Minimum Neues JIT/AOT-Minimum Vorheriges R2R-Ziel Neues R2R-Ziel
Apfel Apple M1 (Keine Änderung) Apple M1 (Keine Änderung)
Linux armv8.0-a (Keine Änderung) armv8.0-a armv8.0-a + LSE
Windows armv8.0-a armv8.0-a + LSE armv8.0-a armv8.2-a + RCPC

x86/x64-Anforderungen

Für alle drei Betriebssysteme (Apple, Linux und Windows) wird der Basisplan von x86-64-v1 auf x86-64-v2 aktualisiert. Dies ändert die Hardware dahingehend, dass sie nicht nur CMOV, CX8, SSE und SSE2 garantiert, sondern auch CX16, POPCNT, SSE3, SSSE3, SSE4.1 und SSE4.2. Diese Garantie wird von Windows 11 und von allen x86/x64-CPUs benötigt, die offiziell auf Windows 10 unterstützt werden. Es umfasst alle Chips, die noch offiziell von Intel und AMD unterstützt werden, wobei die letzten älteren Chips um 2013 aus der Unterstützung gefallen sind.

Das ReadyToRun-Ziel wurde auf x86-64-v3 für Windows und Linux aktualisiert, die zusätzlich die AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT und MOVBE Anweisungssätze enthält. Das ReadyToRun-Ziel für Apple bleibt unverändert.

OS Vorheriges JIT/AOT-Minimum Neues JIT/AOT-Minimum Vorheriges R2R-Ziel Neues R2R-Ziel
Apfel x86-64-v1 x86-64-v2 x86-64-v2 (Keine Änderung)
Linux x86-64-v1 x86-64-v2 x86-64-v2 x86-64-v3
Windows x86-64-v1 x86-64-v2 x86-64-v2 x86-64-v3

Auswirkung

Ab .NET 11 kann .NET auf älterer Hardware nicht ausgeführt werden und eine Meldung wie folgt drucken:

Der aktuellen CPU fehlt mindestens ein Grundbefehlssatz oder mehrere.

Für ReadyToRun-fähige Assemblys gibt es möglicherweise zusätzlichen Startaufwand für einige unterstützte Hardware, die nicht den erwarteten Support für ein typisches Gerät erfüllt.

Grund für Änderung

.NET unterstützt eine breite Palette von Hardware, häufig über und über die Mindesthardwareanforderungen, die vom zugrunde liegenden Betriebssystem bereitgestellt werden. Diese Unterstützung fügt der Codebasis erhebliche Komplexität hinzu, insbesondere für viel ältere Hardware, die wahrscheinlich noch nicht verwendet wird. Darüber hinaus definiert er einen "niedrigsten gemeinsamen Nenner", auf den AOT-Ziele standardmäßig festgelegt werden müssen, was in einigen Szenarien zu einer verringerten Leistung führen kann.

Das Update auf die Mindestbasis wurde vorgenommen, um die Wartungskomplexität der Codebasis zu verringern und die dokumentierten (und häufig erzwungenen) Hardwareanforderungen des zugrunde liegenden Betriebssystems besser auszurichten.

Weitere Informationen finden Sie unter Aktualisierter Mindesthardwareanforderungen.

Laufzeit-Asynchron

.NET 11 führt runtime-native async (Runtime Async V2) ein, ein wichtiger Schritt zum Ersetzen von vom Compiler generierten asynchronen Zustandscomputern durch laufzeitverwaltete Anhalte- und Wiederaufnahme. Anstelle dessen, dass der Compiler Zustandsmaschinenklassen ausgibt, verfolgt die Laufzeitumgebung selbst die asynchrone Ausführung und sorgt für sauberere Stack-Traces, bessere Debugging-Fähigkeiten und einen geringeren Overhead.

Laufzeit-Async ist eine Vorschaufunktion. Um sich zu registrieren, fügen Sie die folgende Eigenschaft zur Projektdatei hinzu:

<PropertyGroup>
  <Features>runtime-async=on</Features>
</PropertyGroup>

Ab Vorschau 3 wird bei einem net11.0-Projekt nicht mehr <EnablePreviewFeatures>true</EnablePreviewFeatures> benötigt, um Runtime-Async zu verwenden.

Sauberere Live-Stack-Ablaufverfolgungen

Die sichtbarste Verbesserung ist in Live-Stapelablaufverfolgungen – was Profiler, Debuggers und new StackTrace() während der Ausführung sehen. Bei vom Compiler generierten asynchronen Methoden erzeugt jede asynchrone Methode mehrere Frames aus der Zustandsmaschineninfrastruktur. Bei Runtime Async werden die tatsächlichen Methoden direkt im Aufrufstapel angezeigt.

// To enable runtime async, add the following to your .csproj:
//   <Features>runtime-async=on</Features>

await OuterAsync();

static async Task OuterAsync()
{
    await Task.CompletedTask;
    await MiddleAsync();
}

static async Task MiddleAsync()
{
    await Task.CompletedTask;
    await InnerAsync();
}

static async Task InnerAsync()
{
    await Task.CompletedTask;
    Console.WriteLine(new StackTrace(fNeedFileInfo: true));
}

Ohne runtime-async-13 Frames, Infrastruktur der Zustandsmaschine sichtbar:

   at Program.<<Main>$>g__InnerAsync|0_2() in Program.cs:line 24
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
   at Program.<<Main>$>g__InnerAsync|0_2()
   at Program.<<Main>$>g__MiddleAsync|0_1() in Program.cs:line 14
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
   at Program.<<Main>$>g__MiddleAsync|0_1()
   at Program.<<Main>$>g__OuterAsync|0_0() in Program.cs:line 8
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
   at Program.<<Main>$>g__OuterAsync|0_0()
   at Program.<Main>$(String[] args) in Program.cs:line 3
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](...)
   at Program.<Main>$(String[] args)
   at Program.<Main>(String[] args)

Mit runtime-async-5 Frames, die tatsächliche Aufrufkette:

   at Program.<<Main>$>g__InnerAsync|0_2() in Program.cs:line 24
   at Program.<<Main>$>g__MiddleAsync|0_1() in Program.cs:line 14
   at Program.<<Main>$>g__OuterAsync|0_0() in Program.cs:line 8
   at Program.<Main>$(String[] args) in Program.cs:line 3
   at Program.<Main>(String[] args)

Hinweis

Ausnahmestack-Traces (von catch (Exception ex)) sehen bereits mit oder ohne Runtime Async gleich aus, da die bestehende ExceptionDispatchInfo-Bereinigung im vom Compiler generierten Code diesen Fall abdeckt. Die Verbesserung ist in dem, was Sie während der Liveausführung sehen.

Diese Verbesserung kommt allem zugute, das den Live-Ausführungsstapel inspiziert, einschließlich Profiling-Tools, Diagnoseprotokollierung und des Debugger-Callstack-Fensters.

NativeAOT- und ReadyToRun-Unterstützung

Preview 3 fügt Runtime-Async-Unterstützung für nativeAOT- und ReadyToRun-Kompilierung hinzu. Dadurch wird das Feature über JIT-kompilierten Code hinaus auf vorab kompilierte Szenarien erweitert. Die Laufzeitumgebung nutzt außerdem Fortsetzungsobjekte intensiver wieder und vermeidet das Speichern unveränderter lokaler Variablen, wodurch der Speicherbedarf in asynchronaufwändigem Code reduziert wird.

Debuggingverbesserungen

Haltepunkte werden jetzt korrekt innerhalb von Runtime-Async-Methoden gebunden, und der Debugger kann await-Grenzen schrittweise durchlaufen, ohne in die vom Compiler generierte Infrastruktur zu springen.

JIT-Verbesserungen

  • Eliminierung von Bounds-Checks: Der Just-in-Time-Compiler (JIT) eliminiert jetzt Bounds-Checks für das gängige Muster, bei dem ein Index plus eine Konstante mit einer Länge verglichen wird, wie z. B. i + cns < len. Außerdem werden redundantere Begrenzungsprüfungen für den Index-von-Ende-Zugriff (z. B. values[^1]) entfernt. Diese Verbesserungen reduzieren redundante Prüfungen in engen Schleifen und verbessern den Durchsatz für Array- und Span-Operationen.
  • Entfernung redundanter geprüfter Kontexte: Der JIT kann jetzt redundante geprüfte arithmetische Kontexte nachweisen und entfernen - beispielsweise wenn bereits bekannt ist, dass ein Wert im Bereich liegt. Durch diese Optimierung werden unnötige Überlaufüberprüfungen im generierten Code beseitigt.
  • Switch-Ausdruckszusammenklappen: Multi-Target-switch-Ausdrücke lassen sich jetzt in einfachere verzweigungsfreie Prüfungen zusammenklappen, wenn die Ziele eine kleine Menge von Konstanten sind, zum Beispiel x is 0 or 1 or 2 or 3 or 4.
  • Schnellere Typumwandlungen von uint zu float/double: Die Umwandlung von uint zu float oder double ist auf x86-Hardware vor AVX-512 schneller.
  • Devirtualisierung in ReadyToRun-Bildern: ReadyToRun (R2R)-Images können jetzt nicht gemeinsam genutzte virtuelle Methodenaufrufe devirtualisieren und die Leistung von vorab kompiliertem Code für generische Szenarien verbessern.
  • SVE2-Intrinsics: Neue Arm SVE2 (Scalable Vector Extension 2) Intrinsics sind verfügbar: ShiftRightLogicalNarrowingSaturate(Even|Odd). Diese erweitern den Satz von vektorisierten Vorgängen, die auf Arm-Hardware verfügbar sind, die SVE2 unterstützt.

Verbesserungen des virtuellen Computers

  • Zwischengespeicherter Schnittstellenaufruf auf Nicht-JIT-Plattformen: Auf Plattformen, die keine JIT-Unterstützung bieten, wie z. B. iOS, wurde der Schnittstellenaufruf zu einem teuren generischen Korrekturpfad zurückgeführt. Cached Dispatch führt bei diesen Zielen zu bis zu 200-fachen Verbesserungen bei Schnittstellen-intensivem Code.
  • Guid.NewGuid() unter Linux:Guid.NewGuid() Unter Linux wird jetzt der getrandom()-Systemaufruf mit Batch-Caching verwendet, anstatt aus /dev/urandom zu lesen, was zu einer Verbesserung des Durchsatzes von etwa 12 % bei der GUID-Generierung führt.

WebAssembly-Verbesserungen

Vorschau 3 erweitert browser- und WebAssembly-Unterstützung mit mehreren Verbesserungen:

  • Laden der WebCIL-Nutzlast: Die Laufzeit kann jetzt WebCIL-Nutzlasten direkt laden und die Kompatibilität mit browserbasierten Bereitstellungsszenarien verbessern.
  • Verbesserte Debuggingsymbole: Die Symbolqualität und Stack-Trace-Qualität für das WebAssembly-Debugging wurden verbessert, um die Diagnose von Problemen in browsergehosteten .NET-Apps zu erleichtern.
  • float[], Span<float> und ArraySegment<float>-Mmarshaling:float[], Span<float> und ArraySegment<float> werden jetzt direkter über JavaScript-Grenzen hinweg gemarshalled, wodurch der Overhead für interoperabilitätsintensiven Code reduziert wird.

Siehe auch