Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden neue Features in der .NET Laufzeit für .NET 11 beschrieben. Es wurde zuletzt für Preview 7 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 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>
Ein net11.0 Projekt erfordert <EnablePreviewFeatures>true</EnablePreviewFeatures> nicht mehr, um Runtime Async zu verwenden.
Die .NET Laufzeitbibliotheken selbst werden mit runtime-async=on kompiliert. Die Laufzeitbibliotheken enthalten keine vom Compiler generierten Zustandsmaschinen mehr und stützen sich vollständig auf die von der Laufzeit bereitgestellte asynchrone Funktionalität. Dies ermöglicht es, eine gesamte App (nur mit Bibliotheksabhängigkeiten) zum neuen Modell zu migrieren und bietet eine umfassende Funktions- und Leistungsüberprüfung des Features. Das Produktteam begrüßt alle Berichte – positiv oder negativ – über Änderungen der Durchsatz- und Bibliotheksgröße, die Sie beobachten.
.NET 11 umfasst auch zwei zusätzliche Verbesserungen:
-
Kovariante
Task→Task<T>-Überschreibungen: Wenn eine abgeleitete Klasse für eine BasismethodeTask<T>zurückgibt, dieTaskzurückgibt, generiert die Laufzeit jetzt einen Thunk mit void-Rückgabewert, der den Unterschied in der Aufrufkonvention überbrückt, sodass der virtuelle Dispatch für beide Varianten funktioniert. Derselbe Fix gilt für NativeAOT. - Inlining in crossgen2: Einschränkungen, die verhinderten, dass Laufzeit-asynchrone Methoden während der ReadyToRun (R2R)-Kompilierung inlineiert werden, wurden entfernt. Alle asynchronen Tests werden sowohl mit Crossgen2 als auch mit Composite R2R bestanden, und das Inlining von asynchronen Aufrufen ohne „await“ (der synchrone Fast-Path) wurde durchgängig bestätigt.
Hinweis
Die DOTNET_RuntimeAsync Variablen und UNSUPPORTED_RuntimeAsync Umgebungsvariablen, die zuvor das asynchrone Laufzeitverhalten gesteuert haben, wurden entfernt. Wenn Sie die asynchrone Laufzeit pro Projekt deaktivieren möchten, legen Sie sie in der Projektdatei fest <UseRuntimeAsync>false</UseRuntimeAsync> , anstatt sich auf die Umgebungsvariable zu verlassen.
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
Runtime Async unterstützt NativeAOT und ReadyToRun-Kompilierung. 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.
Verbesserungen der Asynchronen Laufzeitleistung
Laufzeit-Async umfasst mehrere Leistungsverbesserungen:
- JIT asynchrone Unterstützung: Das JIT kompiliert eine dedizierte Laufzeit-asynchrone Version einer synchronen Methode zur Aufgabenrückgabe, anstatt durch ein Thunk zu delegieren. Der JIT wandelt die Tailcalls der Methode in zur Laufzeit asynchrone Aufrufe um und wartet die Task ab, die andernfalls zurückgegeben würde, wodurch eine zusätzliche Indirektionsebene entfällt.
- Am Ende zusammengeführte Unterbrechungspunkte: Der JIT führt asynchrone Unterbrechungspunkte am Ende zusammen, sodass der generierte Code kompakter ist.
- Zwischengespeicherte Fortsetzungen: Fortsetzungen, die für Thunks von zur Laufzeit asynchron aufrufbaren Aufgaben verwendet werden, werden zwischengespeichert und wiederverwendet.
- Gepoolte Methoden werden ausgenommen: Methoden, die bereits gepoolt sind, werden von Runtime-Async ausgenommen, wodurch doppelte Arbeit vermieden wird.
- Stufenweise Kompilierung für asynchrone Methoden: Asynchrone Versionen durchlaufen jetzt ebenfalls die stufenweise Kompilierung, sodass häufig verwendete asynchrone Methoden nach der Aufwärmphase Tier-1-Optimierungen erhalten.
-
Intrinsische Task- und ValueTask-Factory-Funktionen: Der JIT erkennt gängige Factorys, wie z. B.
Task.FromResult,Task.CompletedTaskundValueTask.FromResult, und führt sie auf schnellere asynchrone Codepfade zurück. -
Tail-await-Optimierungen: Implizite Tailcalls aus asynchronen Methoden werden wieder aktiviert, wenn die Methode direkt ein anderes asynchrones Ergebnis zurückgibt und
await Task.Yield()zusätzliche Zuordnungen in Laufzeit-asynchronen Pfaden verhindert.
Asynchrone Fortsetzungen ohne ExecutionContext
Asynchrone Fortsetzungsvorgänge können jetzt auf die Speicherung und Wiederherstellung von ExecutionContext verzichten.
ExecutionContext überträgt den Umgebungszustand – etwa AsyncLocal<T>-Werte – über await-Punkte hinweg. Jede Task-Fortsetzung erstellte zuvor eine Momentaufnahme des Kontexts und stellte diesen wieder her, bevor sie ausgeführt wurde, selbst wenn kein AsyncLocal<T>-Zustand verwendet wurde und die Wiederherstellung ein No-Op war.
Die Laufzeit erkennt jetzt, wenn es bei einer Fortsetzung nichts wiederherzustellen gibt, und überspringt den Sicherungs-/Wiederherstellungszyklus vollständig.
Task, Task<T>, ValueTaskund ValueTask<T> alle profitieren von dieser Änderung, ebenso wie der Laufzeit-asynchrone Implementierungspfad. Anwendungen, die ConfigureAwait(false) und AsyncLocal<T> in asynchronen Codepfaden mit hohem Durchsatz sparsam einsetzen, weisen einen geringeren Overhead auf.
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 Beispielx is 0 or 1 or 2 or 3 or 4. -
Schnellere Typumwandlungen von uint zu float/double: Die Umwandlung von
uintzufloatoderdoubleist auf x86-Hardware vor AVX-512 schneller. -
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. -
Math.BigMulauf x64:Math.BigMul(long, long, out long)ist jetzt deutlich schneller auf x64. Das JIT generiert eine einzelneMUL r/m64Anweisung, wenn beide Operanden 64-Bit-Werte sind und der Aufrufer die hohe Hälfte des Ergebnisses anfordert, wodurch der vorherige Hilfsaufruf eliminiert wird. - Single-IG Prologeinschränkung entfernt: Der JIT erfordert nicht mehr, dass der Funktionsprolog in eine einzelne Anweisungsgruppe (IG) passt. Komplexe Prologe mit vielen gespeicherten Registern, großen Stack-Allokationen oder der Einrichtung asynchroner Laufzeitzustände lösen keine Fallback-Pfade mehr aus.
-
SELECT(cond, cns, cns)Faltung: Der JIT faltet jetzt bedingte Auswahloperationen, deren beide Zweige dieselbe Konstante erzeugen, zu genau dieser Konstante zusammen – z. B. wirdcondition ? 42 : 42zu42. Diese Optimierung beseitigt unnötige Vergleiche, die auftreten können, nachdem frühere Optimierungen Verzweigungen vereinheitlicht haben. -
Intrinsische AVX-VNNI-512-Funktionen:System.Runtime.Intrinsics.X86.AvxVnni.V512 stellt 512-Bit-Formen der intrinsischen
AVX-VNNI-Multiplikations-/Additionsfunktionen auf CPUs mitAVX512-VNNI-Unterstützung bereit. -
Sättigende Gleitkommakonvertierungen: Bei ungeprüften
float- unddouble-Konvertierungen in kleine ganzzahlige Typen erfolgt nun eine Sättigung an den Typgrenzen, anstatt durch eine Zwischenkürzung einen Umlauf zu bewirken. -
ARM64
Vector<T>als Referenz für SVE: Wenn die Laufzeit mit ARM-SVE-Unterstützung kompiliert wird, werdenVector<T>Werte als Referenz statt als Wert übergeben, im Einklang mit der ARM-Aufrufkonvention für skalierbare Typen, und ermöglichen so eine bessere Codegenerierung für SVE-intensiven Code.
Für eine bessere Leistung und Codequalität fügt .NET 11 mehrere weitere JIT-Optimierungen hinzu:
Konstantenfaltung von SequenceEqual
Das JIT kann jetzt einen Aufruf von string.Equals oder ReadOnlySpan<T>.SequenceEqual falten, wenn beide Operanden Kompilierzeitkonstanten sind, und den Byte-für-Byte-Vergleich durch das konstante Ergebnis true oder false ersetzen. Dies ist besonders wichtig nach dem Inlining, wenn ein Aufrufer ein weiteres Literal an eine Hilfsfunktion übergibt, die es mit einer bekannten Zeichenfolge vergleicht. Wenn IsAdmin in einen Aufrufer eingefügt wird, der "Guest" übergibt, sieht das JIT "Guest" == "Admin" und faltet es zu false:
static bool IsAdmin(string role) => role == "Admin";
Die Optimierung gilt für Zeichenfolgenliterale, const string Felder und UTF-8-Literale (z. B. "PNG"u8).
Verbesserungen bei der Devirtualisierung
Das JIT kann jetzt generische virtuelle Methoden, einschließlich nicht gemeinsam genutzter generischer virtueller Methoden und gemeinsam genutzter generischer virtueller Methoden, die keine Laufzeitsuche erfordern, entvirtualisieren. Standardschnittstellenmethoden, die für generische Schnittstellen deklariert sind, können jetzt auch entvirtualisiert werden.
Darüber hinaus wird das Ergebnis von Activator.CreateInstance<T>() nun als Typ mit exakter Typangabe behandelt, wodurch der JIT nachfolgende Aufrufe des zurückgegebenen Objekts entvirtualisieren kann.
Diese Verbesserungen ermöglichen weitere nachgelagerte Optimierungen wie z. B. Inlining und die Reduktion konstanter Ausdrücke, wodurch der Runtime-Dispatch-Overhead reduziert und die Leistung verbessert wird. Beispiel:
IfaceGeneric<string> caller = new Caller<string>();
Iface obj = Activator.CreateInstance<Impl>();
caller.DefaultImpl(obj, "test"); // Can be optimized down to Console.WriteLine("test")
interface Iface
{
void GenericMethod<T>(T value);
}
interface IfaceGeneric<T>
{
void DefaultImpl(Iface obj, T value) => obj.GenericMethod(value);
}
class Impl : Iface
{
public void GenericMethod<T>(T value) => Console.WriteLine(value);
}
class Caller<T> : IfaceGeneric<T>
{
}
Hier kann der JIT die genaue Implementierung hinter beiden Schnittstellenaufrufen bestimmen, sie entvirtualisieren, einen Inline entlang der Aufrufkette durchführen und den Code letztlich auf das Äquivalent von Console.WriteLine("test") reduzieren.
Entfernen von Begrenzungsprüfungen nach einer Schutzmaßnahme ohne Span
Der JIT übernimmt nun die length != 0-Zusicherung aus einer Prüfung auf eine leere Spanne und verwendet sie, um nachzuweisen, dass die Grenzprüfung für das erste Element erfolgreich ist:
if (!span.IsEmpty && span[0] == value)
{
// The bounds check on span[0] is now eliminated.
}
Beseitigung redundanter Verzweigungen und Tests
Wenn ein äußeres Prädikat bereits durch einen inneren Zweig impliziert wird, entfernt das JIT nun die redundante äußere Prüfung. Ebenso wird der zweite Test eliminiert, wenn Code einen Wert in einer bedingungsbedingten Bedingung zuweist und dann sofort testet:
if (x > 0)
{
if (x > 1) S(); // The outer x > 0 check is folded away.
}
int y = condition ? 1 : 2;
if (y == 1) A(); else B(); // The y == 1 test is eliminated;
// each branch of the ternary goes directly to A() or B().
Diese Optimierungen sind nach dem Inlining am deutlichsten sichtbar, wenn Schutzmaßnahmen aus verschiedenen Methoden im selben kompilierten Codeblock landen.
Systeminterne Hardware und Codegenerierung
.NET 11 umfasst mehrere neue systeminterne Hardware- und Codegenerierungsverbesserungen:
-
F16C-Beschleunigung für
Half↔floatKonvertierungen auf x64: Wenn die CPU F16C (die meisten AVX2-fähigen Hardware) unterstützt, verwenden Konvertierungen zwischen Half undfloat/doublejetzt die dediziertenvcvtph2ps/vcvtps2phAnweisungen anstelle von Hilfsaufrufen. - Bessere Kostenmodellierung für x86/x64-SIMD: Die Kosten des JIT für Gleitkommaausführung und Codegröße basierten zuvor auf Annahmen aus der x87-Ära. Aktualisierte Kostenmodelle, die moderne SSE/AVX-Hardware berücksichtigen, ermöglichen es dem JIT, rund um SIMD-Code bessere Entscheidungen über Codeverschiebung und die Eliminierung gängiger Teilausdrücke (Common Subexpression Elimination, CSE) zu treffen.
-
Schneller
DotProductauf AVX: Die Absenkung für Operationen im Stil vonVector128.Doterzeugt jetzt einemul + permute + add-Sequenz stattvdpps/vdppd, wenn AVX verfügbar ist, was durchgängig schneller ist. -
Schneller
IndexOfAnyAsciiSearcherauf Arm64: Arm64-Versionen vonVector*.Count,IndexOfundLastIndexOfwerden nicht mehr überExtractMostSignificantBitsgeleitet, was bei Workloads, die diese APIs in ihrer Hauptschleife verwenden, zu einer Leistungssteigerung von 5–50 % führt. -
Arm64
ToScalarfür 64-Bit-Ganzzahlen:ToScalarbeiVector*<long>undVector*<ulong>wird jetztfmovanstelle vonumovverwendet, was auf den meisten Kernen kürzer und schneller ist. -
SVE
CreateWhileAPI-Erweiterung:CreateWhilewird um signierte,double- undsingle-Varianten neben den bestehenden unsignierten erweitert und vervollständigt damit den Funktionsumfang zur Prädikatgenerierung für SVE-Schleifen. -
Neue SVE2- und Arm64-Instuktionssatzerkennung: Die Laufzeit meldet jetzt
SVE_AES,SVE_SHA3,SVE_SM4,SHA3undSM4als separate Instruktionssätze und ermöglichtSve2*.IsSupported-Abfragen für diese Features auf geeigneter Hardware. - AVX10-Erkennungskorrektur: Der CPUID-Bit-Cache für AVX10 wurde von einer späteren Abfrage überschrieben, was dazu führen konnte, dass AVX10 auf fähiger Hardware falsch gemeldet wird. Dieses Problem wurde behoben.
ReadyToRun-Verbesserungen
Comparer<T>.Default und EqualityComparer<T>.Default sind jetzt auf ReadyToRun (R2R)-Bilder spezialisiert. Zuvor verwendeten die Standard-Comparer Reflexion, die R2R nicht im Voraus erkennen konnte, sodass Aufrufer auf den JIT zurückgreifen mussten. R2R generiert nun eine spezialisierte Hilfe im Abbild – analog zum NativeAOT-Ansatz – und Benchmarks zeigen eine bis zu 20-fache Leistungssteigerung bei Sammlungsoperationen, die auf der Standardvergleichsfunktion basieren.
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 dergetrandom()-Systemaufruf mit Batch-Caching verwendet, anstatt aus/dev/urandomzu lesen, was zu einer Verbesserung des Durchsatzes von etwa 12 % bei der GUID-Generierung führt.
WebAssembly-Verbesserungen
Browser- und WebAssembly-Unterstützung hat mehrere 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>undArraySegment<float>-Mmarshaling:float[],Span<float>undArraySegment<float>werden jetzt direkter über JavaScript-Grenzen hinweg gemarshalled, wodurch der Overhead für interoperabilitätsintensiven Code reduziert wird.
.NET enthält Verbesserungen an CoreCLR-on-WebAssembly:
-
WebCIL V1 ist die Standardeinstellung für CoreCLR WASM-Builds: Der freigegebene WebCIL-Header erhält ein
TableBaseFeld (28 → 32 Byte). Sowohl Mono- als auch CoreCLR-Leser akzeptieren V0 und V1. DerWasmObjectWritervon Crossgen2 erzeugt direkt V1, und das CoreCLR-basierte WASM-SDK-Build legt die standardmäßigeWasmWebcilVersionaufV1fest. -
Native-Neuverknüpfung wird für CoreCLR-WASM-Apps unterstützt: Eine vollständige Emscripten-basierte Pipeline ersetzt die bisherigen Stub-Targets. Das erneute Verknüpfen über
dotnet.native.wasmaus dem Laufzeitpaket und das Einbinden von benutzerdefiniertem nativem Code überNativeFileReferencefunktioniert jetzt. - JavaScript-Minimierung in Releasebuilds: Browser CoreCLR Release-Builds liefern minimiertes JavaScript.
-
NativeAOT-Veröffentlichung für WASM verwirft keine Paketsatelliten mehr: Satellitenassemblys aus NuGet-Paketen werden jetzt an ILC übergeben und aus der Veröffentlichungsausgabe entfernt, wodurch die Lokalisierung für mit AOT veröffentlichte Apps behoben wird, die von Paketen wie
System.CommandLineabhängen.
CoreCLR auf WebAssembly führt jetzt die Bibliotheks-Testsuite durchgängig aus.
Plattformunterstützung für mehr als 1024 CPUs
Die .NET Laufzeit kann jetzt auf Computern mit mehr als 1024 logischen Prozessoren initialisiert werden. Zuvor wurde sched_getaffinity mit dem Standardwert cpu_set_t (begrenzt auf 1024) aufgerufen, was dazu führte, dass die Initialisierung auf Servern mit vielen Kernen fehlschlug. Die Laufzeit weist den CPU-Satz jetzt dynamisch zu. Der GC behält seinen 1024-Heap-Grenzwert bei, aber der GRENZWERT für die CPU-Anzahl wird entfernt.
Protokollierung prozessinterner Absturzberichte
Ein neuer Prozess-Absturzberichtsmechanismus erfasst Diagnoseinformationen aus dem Absturzprozess, bevor er beendet wird. Zuvor wurden Absturzdiagnosedaten von einem prozessunabhängigen Monitor erfasst. Während der Out-of-Process-Ansatz sicher ist, kann er Informationen übersehen, die nur im Prozess verfügbar sind, der gerade beendet wird. Der neue In-Process-Pfad protokolliert die verwaltete Stapelablaufverfolgung, die Modulliste und wichtige Laufzeitstatusinformationen unter einem bekannten Pfad, bevor der Prozess beendet wird.
Diese Funktion ist spezifisch für mobile Plattformen.
NativeAOT: schnellere Schnittstellenverteilung
NativeAOT verwendet jetzt ein Dispatch-Hilfsprogramm für Schnittstellenmethodenaufrufe. Anstelle einer direkten Fat-Pointer-Aufrufsequenz leitet die Laufzeitumgebung die Schnittstellendispatch über eine gemeinsame Hilfsroutine, die nach dem Aufwärmvorgang der Aufrufstelle auf die richtige Implementierung gepatcht werden kann. Dadurch wird die binäre Größe von Schnittstellenaufrufwebsites reduziert und der Durchsatz für Workloads mit vielen Schnittstellenmethodenaufrufen verbessert.
Der generische virtuelle Methodendispatch verwendet ebenfalls die gemeinsame Dispatch-Cell-Infrastruktur, was sowohl die monomorphe als auch die polymorphe Aufrufleistung verbessert. Darüber hinaus funktioniert Assembly.GetCallingAssembly() jetzt auch unter NativeAOT, wenn StackTrace-Daten verfügbar sind.
SIMD-Lane-Konstruktion und -Zusammensetzung
System.Runtime.Intrinsics enthält nun APIs zur Konstruktion und Zusammensetzung von Lanes für Hardware-Vektortypen. Mit den neuen APIs können Sie einen Vektor aus einzeln festgelegten Elementen erstellen sowie Elemente aus Vektoren extrahieren oder zwischen Vektoren neu anordnen. Dies ermöglicht eine präzise, tragbare Kontrolle über die PLATZIERUNG von SIMD-Vektorelementen, ohne auf plattformspezifische systemspezifische Elemente zurückfallen zu müssen.
Die neuen Methoden lassen sich in wenige Kategorien einteilen:
-
Musterkonstruktion:
CreateGeometricSequence,CreateAlternatingSequenceundCreateHarmonicSequenceerstellen Sie einen Vektor aus einem Anfangswert und einer Regel. -
Verschachteln und entflechten:
Zip,ZipLower/ZipUpper,Unzip,UnzipEven/UnzipOdd. -
Neu anordnen: Die
ConcatFamilie (ConcatLowerLower,ConcatLowerUpper,ConcatUpperLower,ConcatUpperUpper) undReverse.
using System.Runtime.Intrinsics;
// {1, 2, 4, 8} — each lane is the previous lane times two
Vector128<int> powers = Vector128.CreateGeometricSequence(1, 2);
// Interleave two vectors lane-by-lane
(Vector128<int> lower, Vector128<int> upper) =
Vector128.Zip(Vector128.Create(1), Vector128.Create(2));
Diese APIs sind auf Vector128<T>, Vector256<T>, Vector512<T>, Vector64<T> und Vector<T> verfügbar. Sie sind Bausteine für die Bildverarbeitung, die digitale Audiosignalverarbeitung (Digital Signal Processing, DSP) und andere SIMD-intensive Workloads, die eine differenzierte Kontrolle über das Vektorelementlayout benötigen.
Laufzeitdiagnosen
Der asynchrone Profiler instrumentiert jetzt sowohl laufzeit-asynchrone Methoden als auch compilergenerierte asynchrone Zustandsautomatmethoden, sodass Tools unabhängig vom asynchronen Implementierungsstil ein einheitliches Ereignismodell erhalten. Die Laufzeit fügt außerdem eine verbesserte Unterstützung für die perf-map-Protokollierung des Interpreters hinzu und behebt Parsingprobleme mit CORECLR_NOTIFICATION_PROFILERS-Listen, die nicht mit einem Semikolon enden.