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.
Von Bedeutung
Die in diesem Abschnitt beschriebenen Techniken verbessern die Leistung, wenn sie auf heiße Pfade in Ihrem Code angewendet wird. Hot paths sind diejenigen Abschnitte deines Codebestands, die in normalen Abläufen häufig und wiederholt ausgeführt werden. Das Anwenden dieser Techniken auf Code, der nicht häufig ausgeführt wird, hat minimale Auswirkungen. Bevor Sie Änderungen vornehmen, um die Leistung zu verbessern, ist es entscheidend, einen Ausgangswert zu messen. Analysieren Sie dann diese Baseline, um zu bestimmen, wo Speicherengpässe auftreten. Im Abschnitt " Diagnose und Instrumentierung" erfahren Sie mehr über viele plattformübergreifende Tools, um die Leistung Ihrer Anwendung zu messen. Sie können eine Profilerstellungssitzung im Lernprogramm üben, um die Speicherauslastung in der Visual Studio-Dokumentation zu messen.
Nachdem Sie die Speicherauslastung gemessen und festgestellt haben, dass Sie Zuordnungen reduzieren können, verwenden Sie die Techniken in diesem Abschnitt, um Zuordnungen zu reduzieren. Messen Sie nach jeder nachfolgenden Änderung die Speicherauslastung erneut. Stellen Sie sicher, dass jede Änderung einen positiven Einfluss auf die Speicherauslastung in Ihrer Anwendung hat.
Leistungsarbeit in .NET bedeutet häufig das Entfernen von Zuordnungen aus Ihrem Code. Jeder Speicherblock, den Sie zuweisen, muss schließlich freigegeben werden. Weniger Zuweisungen reduzieren den Zeitaufwand für die Garbage Collection. Es bietet die Möglichkeit, die Ausführungszeit besser vorherzusagen, indem Garbage Collections aus bestimmten Code-Pfaden entfernt werden.
Eine häufige Taktik zur Reduzierung von Allokationen besteht darin, kritische Datenstrukturen von class
Typen in struct
Typen zu ändern. Diese Änderung wirkt sich auf die Semantik der Verwendung dieser Typen aus. Parameter und Rückgaben werden jetzt als Wert statt als Referenz übergeben. Die Kosten für das Kopieren eines Werts sind vernachlässigbar, wenn die Typen klein, drei Wörter oder weniger sind (wenn man berücksichtigt, dass ein Wort von einer natürlichen Größe einer ganzen Zahl ist). Es ist messbar und kann echte Auswirkungen auf die Leistung für größere Typen haben. Um den Effekt des Kopierens zu bekämpfen, können Entwickler diese Typen durch ref
übergeben, um die beabsichtigte Semantik zurückzuerlangen.
Die C# ref
-Features bieten Ihnen die Möglichkeit, die gewünschte Semantik für struct
Typen auszudrücken, ohne sich negativ auf ihre allgemeine Benutzerfreundlichkeit zu auswirken. Vor diesen Verbesserungen mussten Entwickler auf Konstrukte mit Zeigern und unformatiertem Speicher zurückgreifen unsafe
, um die gleichen Leistungseinbußen zu erzielen. Der Compiler generiert nachweisbar sicheren Code für die neuen ref
zugehörigen Features.
Sicherer Code bedeutet, dass der Compiler mögliche Pufferüberläufe erkennt oder auf nicht zugewiesenen oder freigegebenen Arbeitsspeicher zugreift. Der Compiler erkennt und verhindert einige Fehler.
Übergabe und Rückgabe per Referenz
Variablen in C# speichern Werte. In struct
Typen ist der Wert der Inhalt einer Instanz des Typs. In class
Typen ist der Wert ein Verweis auf einen Speicherblock, der eine Instanz des Typs speichert. Das Hinzufügen des ref
Modifizierers bedeutet, dass die Variable den Verweis auf den Wert speichert. In struct
Typen verweist der Verweis auf den Speicher, der den Wert enthält. Bei class
-Typen verweist die Referenz auf den Storage, der die Referenz auf den Block des Speichers enthält.
In C# werden Parameter für Methoden nach Wert übergeben, und Rückgabewerte werden nach Wert zurückgegeben. Der Wert des Arguments wird an die Methode übergeben. Der Wert des Rückgabearguments ist der Rückgabewert.
Der ref
, in
, ref readonly
, oder out
Modifizierer gibt an, dass das Argument durch Verweis übergeben wird. Ein Verweis auf den Speicherort wird an die Methode übergeben. Das Hinzufügen ref
zur Methodensignatur bedeutet, dass der Rückgabewert per Verweis zurückgegeben wird. Ein Verweis auf den Speicherort ist der Rückgabewert.
Sie können auch die Verweiszuweisung verwenden, um eine Variable auf eine andere Variable verweisen zu lassen. Eine typische Zuweisung kopiert den Wert der rechten Seite in die Variable auf der linken Seite der Zuweisung. Eine Verweiszuweisung kopiert die Speicherposition der Variablen auf der rechten Seite auf die Variable auf der linken Seite. Das ref
jetzt bezieht sich auf die ursprüngliche Variable:
int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment
Console.WriteLine(location); // output: 42
sameLocation = 19; // assignment
Console.WriteLine(anInteger); // output: 19
Wenn Sie eine Variable zuweisen , ändern Sie den Wert. Wenn Sie einer Variablen Referenzzuweisung geben, ändern Sie die Referenz der Variablen.
Sie können direkt mit dem Storage für Werte arbeiten, indem Sie ref
Variablen, pass by reference und ref Zuweisung verwenden. Vom Compiler erzwungene Bereichsregeln sorgen für Sicherheit beim direkten Arbeiten mit Speicher.
Die ref readonly
und in
Modifizierer geben an, dass das Argument per Referenz übergeben und in der Methode nicht neu zugewiesen werden darf. Der Unterschied besteht darin, dass die Methode ref readonly
den Parameter als Variable verwendet. Die Methode kann den Parameter erfassen oder den Parameter durch eine schreibgeschützte Referenz zurückgeben. In diesen Fällen sollten Sie den ref readonly
Modifizierer verwenden. Andernfalls bietet der in
Modifizierer mehr Flexibilität. Sie müssen den in
Modifizierer nicht zu einem Argument für einen in
Parameter hinzufügen, sodass Sie vorhandene API-Signaturen mithilfe des in
Modifizierers sicher aktualisieren können. Der Compiler gibt eine Warnung aus, wenn Sie nicht entweder den ref
- oder in
-Modifizierer zu einem Argument für einen ref readonly
-Parameter hinzufügen.
Ref sicherer Kontext
C# enthält Regeln für ref
Ausdrücke, um sicherzustellen, dass auf einen ref
Ausdruck nicht zugegriffen werden kann, wenn der Speicher, auf den er verweist, nicht mehr gültig ist. Betrachten Sie das folgenden Beispiel:
public ref int CantEscape()
{
int index = 42;
return ref index; // Error: index's ref safe context is the body of CantEscape
}
Der Compiler meldet einen Fehler, da Sie keinen Verweis auf eine lokale Variable aus einer Methode zurückgeben können. Der Aufrufer kann nicht auf den Speicher zugreifen, auf den verwiesen wird. Der ref-sichere Kontext definiert den Umfang, in dem ein ref
Ausdruck sicher zugegriffen oder geändert werden kann. In der folgenden Tabelle sind die referenzsicheren Kontexte für Variablentypen aufgeführt.
ref
Felder können nicht in einem class
oder einer Nicht-Referenz struct
deklariert werden, daher sind diese Zeilen nicht in der Tabelle enthalten.
Erklärung | Referenzsicherer Kontext |
---|---|
nicht-ref lokal | Block, in dem eine lokale Variable deklariert wird |
nicht-ref Parameter | aktuelle Methode |
ref , ref readonly , in Parameter |
aufrufende Methode |
out -Parameter |
aktuelle Methode |
class -Feld |
aufrufende Methode |
nicht-ref struct Feld |
aktuelle Methode |
ref Feld von ref struct |
aufrufende Methode |
Eine Variable kann ref
zurückgegeben werden, wenn ihr ref sicherer Kontext die aufrufende Methode ist. Wenn ihr ref sicherer Kontext die aktuelle Methode oder eine Blockierung ist, ref
ist die Rückgabe nicht erlaubt. Der folgende Codeausschnitt zeigt zwei Beispiele. Auf ein Mitgliedsfeld kann aus dem Bereich, der eine Methode aufruft, zugegriffen werden. Der ref safe context eines Klassen- oder Struct-Feldes ist also die aufrufende Methode. Der ref sichere Kontext für einen Parameter mit den ref
oder in
Modifikatoren ist die gesamte Methode. Beide können ref
von einer Mitglied-Methode zurückgegeben werden:
private int anIndex;
public ref int RetrieveIndexRef()
{
return ref anIndex;
}
public ref int RefMin(ref int left, ref int right)
{
if (left < right)
return ref left;
else
return ref right;
}
Hinweis
Wenn der ref readonly
- oder in
-Modifizierer auf einen Parameter angewendet wird, kann dieser Parameter von ref readonly
und nicht von ref
zurückgegeben werden.
Der Compiler stellt sicher, dass ein Verweis dem Ref-sicheren Kontext nicht entkommen kann. Sie können ref
Parameter, ref return
und ref
lokale Variablen sicher verwenden, weil der Compiler erkennt, ob Sie versehentlich Code geschrieben haben, auf den ein ref
Ausdruck zugegriffen werden kann, wenn der Speicher ungültig ist.
Sicherer Kontext und Verweisstruktur
ref struct
Typen erfordern weitere Regeln, um sicherzustellen, dass sie sicher verwendet werden können. Ein ref struct
-Typ kann ref
-Felder enthalten. Dies erfordert die Einführung eines sicheren Kontexts. Für die meisten Typen ist der sichere Kontext die aufrufende Methode. Mit anderen Worten, ein Wert, der kein ref struct
Wert ist, kann immer von einer Methode zurückgegeben werden.
Informell ist der sichere Kontext für ein ref struct
der Bereich, in dem auf alle seine ref
Felder zugegriffen werden kann. Mit anderen Worten, es ist die Schnittmenge der ref sichere Kontext aller ref
Felder. Die folgende Methode gibt ein ReadOnlySpan<char>
an ein Mitgliedsfeld zurück, so dass ihr sicherer Kontext die Methode ist:
private string longMessage = "This is a long message";
public ReadOnlySpan<char> Safe()
{
var span = longMessage.AsSpan();
return span;
}
Im Gegensatz dazu gibt der folgende Code einen Fehler aus, weil sich das ref field
Mitglied des Span<int>
auf das vom Stack zugewiesene Array von Ganzzahlen bezieht. Er kann die Methode nicht verlassen:
public Span<int> M()
{
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
}
return numbers; // Error! numbers can't escape this method.
}
Vereinheitlichen von Speichertypen
Die Einführung von System.Span<T> und System.Memory<T> bietet ein einheitliches Modell für die Arbeit mit Arbeitsspeicher.
System.ReadOnlySpan<T> und System.ReadOnlyMemory<T> bieten schreibgeschützte Versionen für den Zugriff auf den Speicher. Sie alle bieten eine Abstraktion über einen Speicherblock, der ein Array ähnlicher Elemente speichert. Der Unterschied besteht darin, dass Span<T>
und ReadOnlySpan<T>
ref struct
Typen sind, wobei Memory<T>
und ReadOnlyMemory<T>
struct
Typen sind. Spans enthalten ein ref field
. Daher können Instanzen einer Spanne ihren sicheren Kontext nicht verlassen. Der sichere Kontext eines ref struct
ist der sichere Ref-Kontext seines ref field
. Die Implementierung von Memory<T>
und ReadOnlyMemory<T>
entfernt diese Einschränkung. Sie verwenden diese Typen, um direkt auf Speicherpuffer zuzugreifen.
Verbessern Sie die Leistung mit ref-Sicherheit
Die Verwendung dieser Features zur Leistungsverbesserung umfasst die folgenden Aufgaben:
-
Vermeiden Sie Zuordnungen: Wenn Sie einen Typ von einem
class
in einsstruct
ändern, ändern Sie, wie er gespeichert wird. Lokale Variablen werden im Stapel gespeichert. Mitglieder werden inline gespeichert, wenn das Container-Objekt alloziert wird. Diese Änderung bedeutet weniger Zuordnungen und verringert die Arbeit, die der Garbage Collector ausführt. Es kann auch den Speicherdruck verringern, so dass der Garbage Collector weniger oft ausgeführt wird. -
Referenzsemantik beibehalten: Das Ändern eines Typs von einem
class
zu einemstruct
verändert die Semantik beim Übergeben einer Variablen an eine Methode. Code, der den Status seiner Parameter geändert hat, muss geändert werden. Nachdem der Parameter einstruct
ist, ändert die Methode eine Kopie des ursprünglichen Objekts. Sie können die ursprüngliche Semantik wiederherstellen, indem Sie diesen Parameter alsref
Parameter übergeben. Nach dieser Änderung ändert die Methode das Originalstruct
erneut. -
Vermeiden Sie das Kopieren von Daten: Das Kopieren größerer
struct
Typen kann sich auf die Leistung in einigen Codepfaden auswirken. Sie können auch denref
-Modifikator hinzufügen, um größere Datenstrukturen per Referenz statt per Wert an Methoden zu übergeben. -
Einschränken von Änderungen: Wenn ein
struct
Typ per Verweis übergeben wird, kann die aufgerufene Methode den Zustand der Struktur ändern. Sie können denref
Modifizierer durch dieref readonly
Oderin
Modifizierer ersetzen, um anzugeben, dass das Argument nicht geändert werden kann. Bevorzugen Sieref readonly
, wenn die Methode den Parameter erfasst oder als Read-Only-Referenz zurückgibt. Sie können auchreadonly struct
Typen oderstruct
Typen mitreadonly
Mitgliedern erstellen, um mehr Kontrolle darüber zu bieten, welche Mitglieder einerstruct
geändert werden können. -
Speicher direkt bearbeiten: Einige Algorithmen sind beim Behandeln von Datenstrukturen als Speicherblock mit einer Abfolge von Elementen am effizientesten. Die Typen
Span
undMemory
bieten sicheren Zugriff auf Speicherblöcke.
Keine dieser Techniken erfordert unsafe
Code. Mit Bedacht können Sie Leistungsmerkmale aus sicherem Code abrufen, der zuvor nur mithilfe unsicherer Techniken möglich war. Sie können die Techniken im Tutorial über Reduzierung von Speicherzuweisungen selbst ausprobieren.