Optimierung der Leistung von C#-Anwendungen: Best Practices für .NET

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum C#-Leistung wichtig ist

In der modernen Softwareentwicklung wirkt sich die Anwendungsgeschwindigkeit direkt auf die Benutzererfahrung, die Infrastrukturkosten und die Wettbewerbsfähigkeit des Produkts aus. Die .NET-Plattform und die Sprache C# bieten leistungsstarke Werkzeuge zur Erstellung hochleistungsfähiger Systeme, jedoch kann die falsche Verwendung von Funktionen (z. B. unkontrollierte Speicherverwaltung oder ineffiziente Algorithmen) zu ernsthaften Verzögerungen und übermäßigem Ressourcenverbrauch führen. In diesem Artikel werden wir wichtige Techniken zur Optimierung der Leistung von C#-Anwendungen behandeln, die Ihnen helfen, schnelleren und effizienteren Code zu schreiben.

1. Profiling: Finden Sie den Engpass vor der Optimierung

Bevor Sie Änderungen vornehmen, muss genau bestimmt werden, wo die meiste Zeit verbracht wird. Ohne Profiling riskieren Sie, bereits schnelle Bereiche zu optimieren und die eigentlichen Engpässe zu übersehen.

Profiling-Tools

  • Visual Studio Diagnostic Tools — ein integrierter Profiler zur Analyse von CPU, Arbeitsspeicher und Ausnahmen.
  • dotMemory — ein leistungsstarkes Tool zur Analyse der Speicherverwaltung und zum Auffinden von Speicherlecks.
  • PerfView — ein kostenloses Tool von Microsoft für die tiefgehende .NET-Leistungsanalyse.
  • BenchmarkDotNet — eine Bibliothek für Mikro-Benchmarks einzelner Codeabschnitte.

Beispiel für die Verwendung von BenchmarkDotNet zur Messung der Geschwindigkeit zweier Methoden:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class StringBenchmark
{
    private string[] data = Enumerable.Range(0, 1000).Select(i => $"Item {i}").ToArray();

    [Benchmark]
    public string ConcatenateWithPlus()
    {
        string result = "";
        foreach (var s in data) result += s;
        return result;
    }

    [Benchmark]
    public string ConcatenateWithStringBuilder()
    {
        var sb = new System.Text.StringBuilder();
        foreach (var s in data) sb.Append(s);
        return sb.ToString();
    }
}

// Run: BenchmarkRunner.Run<StringBenchmark>();

Die Benchmark-Ergebnisse werden deutlich zeigen, dass StringBuilder um ein Vielfaches schneller ist als die Verkettung mit + in Schleifen.

2. Speicherverwaltung und Reduzierung der GC-Belastung

Der Garbage Collector (GC) ist ein leistungsstarker Mechanismus, aber sein häufiges Eingreifen kann die Leistung negativ beeinflussen, insbesondere in hochbelasteten Systemen. Das Hauptziel ist es, die Anzahl der Speicherzuweisungen und die Lebensdauer von Objekten zu minimieren.

2.1. Verwendung von Strukturen anstelle von Klassen

Wenn ein Objekt klein (weniger als 16-24 Bytes) ist und eine kurze Lebensdauer hat, erwägen Sie die Verwendung eines struct. Strukturen werden auf dem Stapel (oder im übergeordneten Objekt eingebettet) gespeichert und erzeugen keine GC-Belastung.

// Klasse — Heap-Zuweisung
public class PointClass
{
    public int X { get; set; }
    public int Y { get; set; }
}

// Struktur — auf dem Stapel
public struct PointStruct
{
    public int X;
    public int Y;
}

2.2. Objekt-Pooling

Für häufig erstellte und zerstörte Objekte (z. B. Datenbankverbindungen, Puffer) verwenden Sie einen Pool. .NET Core bietet eine integrierte Implementierung ObjectPool<T>.

using Microsoft.Extensions.ObjectPool;

var pool = new DefaultObjectPool<List<int>>(new ListPolicy());
var list = pool.Get();
try
{
    // Arbeiten mit der Liste
}
finally
{
    pool.Return(list); // Zurück in den Pool
}

2.3. Span<T> und Memory<T>

Diese Typen ermöglichen die Arbeit mit zusammenhängenden Speicherbereichen ohne zusätzliche Zuweisungen. Sie sind ideal für das Parsen und die Arbeit mit Arrays und Zeichenketten.

public int SumDigits(ReadOnlySpan<                

Blogs

Buchempfehlungen