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.
Tutorial: Reduzieren von Speicherzuordnungen mit
Häufig umfasst die Leistungsoptimierung für eine .NET-Anwendung zwei Techniken. Erstens: Reduzieren von Anzahl und Größe der Heapzuordnungen. Reduzieren Sie zweitens, wie oft Daten kopiert werden. Visual Studio bietet großartige Tools , mit denen Sie analysieren können, wie Ihre Anwendung Arbeitsspeicher verwendet. Nachdem Sie festgestellt haben, wo Ihre App unnötige Zuordnungen vorschreibt, nehmen Sie Änderungen vor, um diese Zuordnungen zu minimieren. Sie konvertieren class
Typen in struct
Typen. Sie verwenden ref
Sicherheitsfeatures , um Semantik zu erhalten und zusätzliches Kopieren zu minimieren.
Verwenden Sie Visual Studio 17.5 für eine optimale Erfahrung mit diesem Lernprogramm. Das .NET-Objektzuweisungstool, das zum Analysieren der Speicherauslastung verwendet wird, ist Teil von Visual Studio. Sie können Visual Studio Code und die Befehlszeile verwenden, um die Anwendung auszuführen und alle Änderungen vorzunehmen. Sie können die Analyseergebnisse Ihrer Änderungen jedoch nicht sehen.
Die Anwendung, die Sie verwenden werden, ist eine Simulation einer IoT-Anwendung, die mehrere Sensoren überwacht, um festzustellen, ob ein Eindringling eine geheime Galerie mit Wertgegenständen betreten hat. Die IoT-Sensoren senden ständig Daten, die den Mix aus Sauerstoff (O2) und Kohlendioxid (CO2) in der Luft messen. Sie melden auch die Temperatur und relative Feuchtigkeit. Jeder dieser Werte schwankt immer leicht. Wenn eine Person jedoch in den Raum eintritt, verändert sich die Situation etwas mehr und immer in die gleiche Richtung: Der Sauerstoffgehalt verringert sich, der Kohlendioxidgehalt erhöht sich, die Temperatur steigt, ebenso wie die relative Luftfeuchtigkeit. Wenn die Sensoren eine Zunahme zeigen, wird der Eindringlingsalarm ausgelöst.
In diesem Lernprogramm führen Sie die Anwendung aus, nehmen Messungen für Speicherzuweisungen vor und verbessern dann die Leistung, indem Sie die Anzahl der Zuordnungen verringern. Der Quellcode ist im Beispielbrowser verfügbar.
Erkunden der Startanwendung
Laden Sie die Anwendung herunter, und führen Sie das Startbeispiel aus. Die Startanwendung funktioniert ordnungsgemäß, aber da sie viele kleine Objekte mit jedem Messzyklus zuordnet, verschlechtert sich die Leistung langsam, wenn sie im Laufe der Zeit ausgeführt wird.
Press <return> to start simulation
Debounced measurements:
Temp: 67.332
Humidity: 41.077%
Oxygen: 21.097%
CO2 (ppm): 404.906
Average measurements:
Temp: 67.332
Humidity: 41.077%
Oxygen: 21.097%
CO2 (ppm): 404.906
Debounced measurements:
Temp: 67.349
Humidity: 46.605%
Oxygen: 20.998%
CO2 (ppm): 408.707
Average measurements:
Temp: 67.349
Humidity: 46.605%
Oxygen: 20.998%
CO2 (ppm): 408.707
Viele Zeilen wurden entfernt.
Debounced measurements:
Temp: 67.597
Humidity: 46.543%
Oxygen: 19.021%
CO2 (ppm): 429.149
Average measurements:
Temp: 67.568
Humidity: 45.684%
Oxygen: 19.631%
CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High
Debounced measurements:
Temp: 67.602
Humidity: 46.835%
Oxygen: 19.003%
CO2 (ppm): 429.393
Average measurements:
Temp: 67.568
Humidity: 45.684%
Oxygen: 19.631%
CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High
Sie können den Code erkunden, um zu erfahren, wie die Anwendung funktioniert. Das Hauptprogramm führt die Simulation aus. Wenn Sie <Enter>
drücken, wird ein Raum erstellt und einige anfängliche Basisdaten gesammelt.
Console.WriteLine("Press <return> to start simulation");
Console.ReadLine();
var room = new Room("gallery");
var r = new Random();
int counter = 0;
room.TakeMeasurements(
m =>
{
Console.WriteLine(room.Debounce);
Console.WriteLine(room.Average);
Console.WriteLine();
counter++;
return counter < 20000;
});
Nachdem diese Basisdaten eingerichtet wurden, wird die Simulation im Raum ausgeführt, wobei ein Zufallszahlengenerator bestimmt, ob ein Eindringling den Raum betreten hat.
counter = 0;
room.TakeMeasurements(
m =>
{
Console.WriteLine(room.Debounce);
Console.WriteLine(room.Average);
room.Intruders += (room.Intruders, r.Next(5)) switch
{
( > 0, 0) => -1,
( < 3, 1) => 1,
_ => 0
};
Console.WriteLine($"Current intruders: {room.Intruders}");
Console.WriteLine($"Calculated intruder risk: {room.RiskStatus}");
Console.WriteLine();
counter++;
return counter < 200000;
});
Andere Typen enthalten die Messungen: ein gleitender mittlerer Messwert, der den Mittelwert der letzten 50 Messungen darstellt, und den Mittelwert aller durchgeführten Messungen.
Führen Sie als Nächstes die Anwendung mit dem .NET-Objektzuweisungstool aus. Stellen Sie sicher, dass Sie den Release
Build verwenden, nicht den Debug
Build. Öffnen Sie im Menü "Debuggen " den Performance Profiler. Aktivieren Sie die Option Nachverfolgung der .NET-Objektzuordnung, aber keine andere Option. Führen Sie Ihre Anwendung bis zum Abschluss aus. Der Profiler misst Objektzuordnungen und meldet Zuordnungen und Garbage Collection-Zyklen. Es sollte ein Diagramm wie in der folgenden Abbildung angezeigt werden:
Das vorherige Diagramm zeigt, dass die Bemühungen zur Reduzierung von Zuweisungen Leistungsvorteile bieten. Sie sehen ein Sägezahnmuster im Liveobjektdiagramm. Das sagt Ihnen, dass zahlreiche Objekte erstellt werden, die schnell zu Garbage werden. Sie werden später bereinigt, wie im Deltadiagramm für Objekte gezeigt. Die nach unten zeigenden roten Balken deuten auf einen Müllsammelzyklus hin.
Sehen Sie sich als Nächstes die Registerkarte " Zuordnungen " unter den Diagrammen an. In dieser Tabelle wird gezeigt, welche Typen am häufigsten zugewiesen werden:
Der System.String Typ wird für die meisten Zuordnungen verwendet. Die wichtigste Aufgabe sollte es sein, die Häufigkeit von Speicherzuweisungen für Zeichenfolgen zu minimieren. Diese Anwendung gibt laufend zahlreiche formatierte Ausgaben in der Konsole aus. Für diese Simulation möchten wir Nachrichten beibehalten, daher konzentrieren wir uns auf die nächsten beiden Zeilen: den SensorMeasurement
Typ und den IntruderRisk
Typ.
Doppelklicken Sie auf die SensorMeasurement
Zeile. Sie können sehen, dass alle Zuordnungen in der static
Methode SensorMeasurement.TakeMeasurement
stattfinden. Die Methode wird im folgenden Codeausschnitt angezeigt:
public static SensorMeasurement TakeMeasurement(string room, int intruders)
{
return new SensorMeasurement
{
CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
Room = room,
TimeRecorded = DateTime.Now
};
}
Jedes Maß weist ein neues SensorMeasurement
Objekt zu, bei dem es sich um einen class
Typ handelt. Jede erstellte SensorMeasurement
-Instanz verursacht eine Heapzuordnung.
Ändern von Klassen zu Strukturen
Der folgende Code zeigt die anfängliche Deklaration von SensorMeasurement
:
public class SensorMeasurement
{
private static readonly Random generator = new Random();
public static SensorMeasurement TakeMeasurement(string room, int intruders)
{
return new SensorMeasurement
{
CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
Room = room,
TimeRecorded = DateTime.Now
};
}
private const double CO2Concentration = 409.8; // increases with people.
private const double O2Concentration = 0.2100; // decreases
private const double TemperatureSetting = 67.5; // increases
private const double HumiditySetting = 0.4500; // increases
public required double CO2 { get; init; }
public required double O2 { get; init; }
public required double Temperature { get; init; }
public required double Humidity { get; init; }
public required string Room { get; init; }
public required DateTime TimeRecorded { get; init; }
public override string ToString() => $"""
Room: {Room} at {TimeRecorded}:
Temp: {Temperature:F3}
Humidity: {Humidity:P3}
Oxygen: {O2:P3}
CO2 (ppm): {CO2:F3}
""";
}
Der Typ wurde ursprünglich als ein class
erstellt, da er zahlreiche double
Messungen enthält. Dieser ist größer, als Sie in langsamsten Pfaden kopieren wollten. Diese Entscheidung bedeutete jedoch eine große Anzahl von Zuteilungen. Ändern Sie den Typ von a class
in ein struct
.
Beim Wechsel von einem class
zu struct
treten einige Compilerfehler auf, da der ursprüngliche Code an einigen Stellen null
Referenzüberprüfungen verwendet hat. Der erste befindet sich in der DebounceMeasurement
Klasse, in der AddMeasurement
Methode:
public void AddMeasurement(SensorMeasurement datum)
{
int index = totalMeasurements % debounceSize;
recentMeasurements[index] = datum;
totalMeasurements++;
double sumCO2 = 0;
double sumO2 = 0;
double sumTemp = 0;
double sumHumidity = 0;
for (int i = 0; i < debounceSize; i++)
{
if (recentMeasurements[i] is not null)
{
sumCO2 += recentMeasurements[i].CO2;
sumO2+= recentMeasurements[i].O2;
sumTemp+= recentMeasurements[i].Temperature;
sumHumidity += recentMeasurements[i].Humidity;
}
}
O2 = sumO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
CO2 = sumCO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
Temperature = sumTemp / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
Humidity = sumHumidity / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
}
Der DebounceMeasurement
Typ enthält ein Array von 50 Maßen. Die Messwerte für einen Sensor werden als Mittelwert der letzten 50 Messungen gemeldet. Dadurch wird das Rauschen in den Messwerten reduziert. Bevor alle 50 Messungen durchgeführt wurden, sind diese Werte null
. Der Code führt eine null
-Verweisprüfung durch, um beim Systemstart den richtigen Mittelwert zu melden. Nachdem Sie den SensorMeasurement
Typ in eine Struktur geändert haben, müssen Sie einen anderen Test verwenden. Der SensorMeasurement
Typ enthält einen string
für den Raumbezeichner, sodass Sie stattdessen diesen Test verwenden können:
if (recentMeasurements[i].Room is not null)
Die anderen drei Compilerfehler befinden sich alle in der Methode, die wiederholt Messungen in einem Raum ausführt:
public void TakeMeasurements(Func<SensorMeasurement, bool> MeasurementHandler)
{
SensorMeasurement? measure = default;
do {
measure = SensorMeasurement.TakeMeasurement(Name, Intruders);
Average.AddMeasurement(measure);
Debounce.AddMeasurement(measure);
} while (MeasurementHandler(measure));
}
Bei der Startmethode ist die lokale Variable für die SensorMeasurement
eine nullable Referenz:
SensorMeasurement? measure = default;
Da SensorMeasurement
ein struct
- und kein class
-Typ ist, ist jetzt ein Nullable-Werttyp vorhanden. Sie können die Deklaration in einen Werttyp ändern, um die verbleibenden Compilerfehler zu beheben:
SensorMeasurement measure = default;
Nachdem die Compilerfehler behoben wurden, sollten Sie den Code untersuchen, um sicherzustellen, dass sich die Semantik nicht geändert hat. Da struct
Typen nach Wert übergeben werden, sind Änderungen, die an Methodenparametern vorgenommen werden, nach der Rückkehr der Methode nicht mehr sichtbar.
Von Bedeutung
Das Ändern eines Typs von einem class
in eine struct
kann die Semantik Ihres Programms ändern. Wenn ein class
Typ an eine Methode übergeben wird, werden alle in der Methode vorgenommenen Mutationen an das Argument vorgenommen. Wenn ein struct
-Typ an eine Methode übergeben wird, werden alle in der Methode vorgenommenen Änderungen für eine Kopie des Arguments durchgeführt. Dies bedeutet, dass jede Methode, die ihre Argumente nach Entwurf ändert, aktualisiert werden sollte, um den ref
Modifizierer für jeden Argumenttyp zu verwenden, den Sie von einer class
zu einer struct
geändert haben.
Der SensorMeasurement
Typ enthält keine Methoden, die den Zustand ändern, daher ist dies in diesem Beispiel kein Problem. Sie können das beweisen, indem Sie den readonly
Modifizierer zur SensorMeasurement
Struktur hinzufügen:
public readonly struct SensorMeasurement
Der Compiler erzwingt, dass die readonly
-Struktur schreibgeschützt (SensorMeasurement
) ist. Wenn Ihre Überprüfung des Codes einige Methoden übersehen haben sollte, die den Zustand geändert haben, würde der Compiler Ihnen das mitteilen. Ihre App erstellt weiterhin ohne Fehler, sodass dieser Typ lautet readonly
. Das Hinzufügen des readonly
Modifizierers, wenn Sie einen Typ von class
zu struct
ändern, kann Ihnen helfen, Mitglieder zu finden, die den Zustand von struct
verändern.
Vermeiden des Erstellens von Kopien
Sie haben eine große Anzahl von unnötigen Zuordnungen aus Ihrer App entfernt. Der SensorMeasurement
Typ wird nirgendwo in der Tabelle angezeigt.
Jetzt erfordert das Kopieren der SensorMeasurement
Struktur zusätzliche Arbeit, jedes Mal wenn sie als Parameter oder Rückgabewert verwendet wird. Die SensorMeasurement
Struktur enthält vier Doubles, a DateTime und a string
. Diese Struktur ist deutlich größer als ein Verweis. Fügen wir die ref
in
Modifizierer an Stellen hinzu, an denen der SensorMeasurement
Typ verwendet wird.
Der nächste Schritt besteht darin, Methoden zu finden, die ein Maß zurückgeben oder ein Maß als Argument verwenden und soweit möglich Verweise verwenden. Beginnen Sie in der SensorMeasurement
Struktur. Die statische TakeMeasurement
Methode erstellt und gibt ein neues SensorMeasurement
zurück:
public static SensorMeasurement TakeMeasurement(string room, int intruders)
{
return new SensorMeasurement
{
CO2 = (CO2Concentration + intruders * 10) + (20 * generator.NextDouble() - 10.0),
O2 = (O2Concentration - intruders * 0.01) + (0.005 * generator.NextDouble() - 0.0025),
Temperature = (TemperatureSetting + intruders * 0.05) + (0.5 * generator.NextDouble() - 0.25),
Humidity = (HumiditySetting + intruders * 0.005) + (0.20 * generator.NextDouble() - 0.10),
Room = room,
TimeRecorded = DateTime.Now
};
}
Wir lassen sie unverändert, sodass weiterhin ein Wert zurückgegeben wird. Wenn Sie versuchen, mittels ref
zurückzugeben, erhalten Sie einen Compilerfehler. Eine ref
-Rückgabe an eine neue Struktur, die lokal in der Methode erstellt wurde, ist nicht möglich. Die Konstruktion der unveränderlichen Struktur bedeutet, dass Sie nur die Werte der Messung im Bau festlegen können. Diese Methode muss eine neue Messstruktur erstellen.
Schauen wir uns DebounceMeasurement.AddMeasurement
erneut an. Sie sollten dem Parameter den in
measurement
Modifizierer hinzufügen:
public void AddMeasurement(in SensorMeasurement datum)
{
int index = totalMeasurements % debounceSize;
recentMeasurements[index] = datum;
totalMeasurements++;
double sumCO2 = 0;
double sumO2 = 0;
double sumTemp = 0;
double sumHumidity = 0;
for (int i = 0; i < debounceSize; i++)
{
if (recentMeasurements[i].Room is not null)
{
sumCO2 += recentMeasurements[i].CO2;
sumO2+= recentMeasurements[i].O2;
sumTemp+= recentMeasurements[i].Temperature;
sumHumidity += recentMeasurements[i].Humidity;
}
}
O2 = sumO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
CO2 = sumCO2 / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
Temperature = sumTemp / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
Humidity = sumHumidity / ((totalMeasurements > debounceSize) ? debounceSize : totalMeasurements);
}
Dadurch wird ein Kopiervorgang eingespart. Der in
Parameter ist ein Verweis auf die kopie, die bereits vom Aufrufer erstellt wurde. Sie können eine Kopie auch mit der TakeMeasurement
Methode im Room
Typ speichern. Diese Methode veranschaulicht, wie der Compiler Sicherheit bietet, wenn Sie Argumente über das System ref
übergeben. Die erste TakeMeasurement
Methode im Room
Typ verwendet ein Argument von Func<SensorMeasurement, bool>
. Wenn Sie versuchen, dieser Deklaration den in
ref
Modifizierer hinzuzufügen, meldet der Compiler einen Fehler. Sie können ein ref
Argument nicht an einen Lambda-Ausdruck übergeben. Der Compiler kann nicht garantieren, dass der aufgerufene Ausdruck den Verweis nicht kopiert. Wenn der Lambda-Ausdruck den Verweis erfasst, könnte der Verweis eine längere Lebensdauer haben als der Wert, auf den er verweist. Der Zugriff auf die Datei außerhalb des sicherheitssicheren Kontexts würde zu Speicherbeschädigungen führen. Die ref
Sicherheitsregeln lassen sie nicht zu. Weitere Informationen finden Sie in der Übersicht über Sicherheitsfunktionen von Ref.
Semantik beibehalten
Die letzten Änderungen haben keine großen Auswirkungen auf die Leistung dieser Anwendung, da die Typen nicht in den langsamsten Pfaden erstellt werden. Diese Änderungen veranschaulichen einige der anderen Techniken, die Sie in Ihrer Leistungsoptimierung verwenden sollten. Sehen wir uns die erste Room
Klasse an:
public class Room
{
public AverageMeasurement Average { get; } = new ();
public DebounceMeasurement Debounce { get; } = new ();
public string Name { get; }
public IntruderRisk RiskStatus
{
get
{
var CO2Variance = (Debounce.CO2 - Average.CO2) > 10.0 / 4;
var O2Variance = (Average.O2 - Debounce.O2) > 0.005 / 4.0;
var TempVariance = (Debounce.Temperature - Average.Temperature) > 0.05 / 4.0;
var HumidityVariance = (Debounce.Humidity - Average.Humidity) > 0.20 / 4;
IntruderRisk risk = IntruderRisk.None;
if (CO2Variance) { risk++; }
if (O2Variance) { risk++; }
if (TempVariance) { risk++; }
if (HumidityVariance) { risk++; }
return risk;
}
}
public int Intruders { get; set; }
public Room(string name)
{
Name = name;
}
public void TakeMeasurements(Func<SensorMeasurement, bool> MeasurementHandler)
{
SensorMeasurement? measure = default;
do {
measure = SensorMeasurement.TakeMeasurement(Name, Intruders);
Average.AddMeasurement(measure);
Debounce.AddMeasurement(measure);
} while (MeasurementHandler(measure));
}
}
Dieser Typ enthält mehrere Eigenschaften. Einige sind class
-Typen. Das Erstellen eines Room
Objekts umfasst mehrere Zuordnungen. Eine für Room
selbst und ein für jeden Member eines enthaltenen class
-Typs. Sie können zwei dieser Eigenschaften von class
Typen in struct
Typen konvertieren: die DebounceMeasurement
Typen und die AverageMeasurement
Typen. Lassen Sie uns diese Transformation mit beiden Typen durcharbeiten.
Ändern Sie den DebounceMeasurement
Typ von a class
in struct
. Dadurch wird ein Compilerfehler CS8983: A 'struct' with field initializers must include an explicitly declared constructor
eingeführt. Sie können dies beheben, indem Sie einen leeren parameterlosen Konstruktor hinzufügen:
public DebounceMeasurement() { }
Weitere Informationen zu dieser Anforderung finden Sie im Sprachreferenzartikel zu Strukturen.
Die Object.ToString()-Überschreibung ändert keinen Wert der Struktur. Sie können dieser Methodendeklaration den readonly
Modifizierer hinzufügen. Der DebounceMeasurement
Typ ist änderbar, daher müssen Sie sicherstellen, dass Änderungen sich nicht auf Kopien auswirken, die verworfen werden. Die AddMeasurement
Methode ändert den Status des Objekts. Sie wird aus der Room
Klasse in der TakeMeasurements
Methode aufgerufen. Sie möchten, dass diese Änderungen nach dem Aufrufen der Methode beibehalten werden. Sie können die Eigenschaft Room.Debounce
ändern, sodass sie eine Referenz zu einer einzelnen Instanz des Typs zurückgibt.
private DebounceMeasurement debounce = new();
public ref readonly DebounceMeasurement Debounce { get { return ref debounce; } }
Im vorherigen Beispiel gibt es einige Änderungen. Erstens ist die Eigenschaft eine schreibgeschützte Eigenschaft, die einen schreibgeschützten Verweis auf die zum jeweiligen Raum gehörende Instanz zurückgibt. Es wird jetzt von einem deklarierten Feld unterstützt, das initialisiert wird, wenn das Room
Objekt instanziiert wird. Nachdem Sie diese Änderungen vorgenommen haben, aktualisieren Sie die Implementierung der AddMeasurement
Methode. Sie verwendet das private Unterstützungsfeld, debounce
, und nicht die schreibgeschützte Eigenschaft Debounce
. Auf diese Weise erfolgen die Änderungen an der einzelnen Instanz, die während der Initialisierung erstellt wurde.
Die gleiche Technik funktioniert mit der Average
Eigenschaft. Zuerst ändern Sie den AverageMeasurement
-Typ von einem class
in ein struct
, und fügen den readonly
-Modifizierer zur ToString
-Methode hinzu.
namespace IntruderAlert;
public struct AverageMeasurement
{
private double sumCO2 = 0;
private double sumO2 = 0;
private double sumTemperature = 0;
private double sumHumidity = 0;
private int totalMeasurements = 0;
public AverageMeasurement() { }
public readonly double CO2 => sumCO2 / totalMeasurements;
public readonly double O2 => sumO2 / totalMeasurements;
public readonly double Temperature => sumTemperature / totalMeasurements;
public readonly double Humidity => sumHumidity / totalMeasurements;
public void AddMeasurement(in SensorMeasurement datum)
{
totalMeasurements++;
sumCO2 += datum.CO2;
sumO2 += datum.O2;
sumTemperature += datum.Temperature;
sumHumidity+= datum.Humidity;
}
public readonly override string ToString() => $"""
Average measurements:
Temp: {Temperature:F3}
Humidity: {Humidity:P3}
Oxygen: {O2:P3}
CO2 (ppm): {CO2:F3}
""";
}
Anschließend ändern Sie die Room
Klasse nach demselben Verfahren, das Sie für die Debounce
Eigenschaft verwendet haben. Die Average
-Eigenschaft gibt ein readonly ref
an das private Feld für den Mittelwert der Messungen zurück. Die AddMeasurement
Methode ändert die internen Felder.
private AverageMeasurement average = new();
public ref readonly AverageMeasurement Average { get { return ref average; } }
Boxen vermeiden
Es gibt eine letzte Änderung, um die Leistung zu verbessern. Das Hauptprogramm druckt Statistiken für den Raum, einschließlich der Risikobewertung:
Console.WriteLine($"Current intruders: {room.Intruders}");
Console.WriteLine($"Calculated intruder risk: {room.RiskStatus}");
Der Aufruf von ToString
bewirkt ein Boxing des Enumerationswerts. Sie können dies vermeiden, indem Sie eine Außerkraftsetzung in der Room
Klasse schreiben, die die Zeichenfolge basierend auf dem Wert des geschätzten Risikos formatiert:
public override string ToString() =>
$"Calculated intruder risk: {RiskStatus switch
{
IntruderRisk.None => "None",
IntruderRisk.Low => "Low",
IntruderRisk.Medium => "Medium",
IntruderRisk.High => "High",
IntruderRisk.Extreme => "Extreme",
_ => "Error!"
}}, Current intruders: {Intruders.ToString()}";
Ändern Sie dann den Code im Hauptprogramm, um diese neue ToString
Methode aufzurufen:
Console.WriteLine(room.ToString());
Führen Sie die App mit dem Profiler aus, und sehen Sie sich die aktualisierte Tabelle für Zuordnungen an.
Sie haben zahlreiche Zuordnungen entfernt und Ihre App hat einen Leistungsschub erhalten.
Verwenden der Ref-Sicherheit in Ihrer Anwendung
Bei diesen Techniken handelt es sich um eine Low-Level-Leistungsoptimierung. Sie können die Leistung in Ihrer Anwendung erhöhen, wenn sie auf heiße Pfade angewendet wird, und wenn Sie die Auswirkung vor und nach den Änderungen gemessen haben. In den meisten Fällen lautet der folgende Zyklus:
- Messen Sie Speicherzuweisungen: Bestimmen Sie, welche Speichertypen am häufigsten zugewiesen werden, und wann Sie die Heap-Speicherzuweisungen reduzieren können.
-
Klasse in Struktur konvertieren: Häufig können Typen von einem
class
in einstruct
konvertiert werden. Ihre App verwendet Stapelplatz, anstatt Heap-Zuordnungen vorzunehmen. -
Semantik beibehalten: Das Konvertieren einer
class
in einestruct
kann sich auf die Semantik für Parameter und Rückgabewerte auswirken. Alle Methoden, die ihre Parameter ändern, sollten diese Parameter jetzt mit demref
Modifizierer markieren. Dadurch wird sichergestellt, dass die Änderungen am richtigen Objekt vorgenommen werden. Wenn ein Eigenschafts- oder Methodenrückgabewert vom Aufrufer geändert werden soll, sollte die betreffende Rückgabe ebenfalls mit demref
-Modifizierer gekennzeichnet werden. -
Vermeiden Sie Kopien: Wenn Sie eine große Struktur als Parameter übergeben, können Sie den Parameter mit dem
in
Modifizierer markieren. Sie können einen Verweis in weniger Bytes übergeben und sicherstellen, dass die Methode den ursprünglichen Wert nicht ändert. Sie können auch Werte alsreadonly ref
zurückgeben, um einen Verweis zurückzugeben, der nicht geändert werden kann.
Mithilfe dieser Techniken können Sie die Leistung in heißen Pfaden ihres Codes verbessern.