.NET 8, Kasım 2023'te LTS statüsüyle yayınlandı. Bu sürümde Microsoft yalnızca yeni özellikler eklemekle kalmadı; runtime, JIT derleyici, GC ve standart kütüphane genelinde yüzlerce performans iyileştirmesi de gerçekleştirdi. Gelin en kritik başlıkları inceleyelim.
1. Dynamic PGO Varsayılan Olarak Açık
Profile-Guided Optimization (PGO), kodu derlemeden önce profil verisi toplayarak daha iyi makine kodu üretme tekniğidir. .NET 8'de Dynamic PGO artık varsayılan olarak aktif.
Klasik PGO'dan farkı şu: Dinamik PGO, uygulamanın çalışma anında hangi kod yollarının sık kullanıldığını öğrenir ve JIT'i buna göre yeniden derler. Bu işleme re-JIT denir.
// Bu kod .NET 8'de çok daha hızlı çalışır:
// PGO, Process() metodunun gerçekte hangi tipte çağrıldığını öğrenir
// ve devirtualization + inlining uygular
foreach (IProcessor processor in processors)
{
processor.Process(item); // Sanal çağrı → doğrudan çağrıya dönüşebilir
}
// Aynı şekilde LINQ chain'leri de optimize edilir
var result = data
.Where(x => x.IsActive)
.Select(x => x.Value)
.Sum();
Telemetre verilerine göre Dynamic PGO ile gerçek dünya uygulamalarında %10–40 throughput artışı gözlemlendi. Startup süresi biraz uzayabilir ancak uzun süre çalışan servisler için net kazanç çok daha büyük.
2. FrozenDictionary ve FrozenSet
System.Collections.Frozen namespace'i ile gelen bu koleksiyonlar, bir kez oluşturulup defalarca okunacak veriler için özel olarak optimize edildi.
// Normal Dictionary ile karşılaştırma
var regular = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["GET"] = 1,
["POST"] = 2,
["PUT"] = 3,
["DELETE"] = 4,
["PATCH"] = 5
};
// FrozenDictionary: oluşturma maliyeti var, ama okuma çok daha hızlı
FrozenDictionary<string, int> frozen = regular.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
// Benchmark sonuçları (BenchmarkDotNet):
// Dictionary.TryGetValue : 15.2 ns
// FrozenDictionary.TryGetValue : 8.7 ns (~%43 daha hızlı)
// Uygulama içinde doğru kullanım
public class HttpMethodRegistry
{
// Singleton olarak bir kez oluştur
private static readonly FrozenDictionary<string, HttpMethod> _methods =
new Dictionary<string, HttpMethod>
{
["GET"] = HttpMethod.Get,
["POST"] = HttpMethod.Post,
["PUT"] = HttpMethod.Put,
["DELETE"] = HttpMethod.Delete
}.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
public static HttpMethod? Resolve(string name)
=> _methods.TryGetValue(name, out var method) ? method : null;
}
3. SearchValues<T>: Hızlı Karakter/Byte Arama
Bir string içinde belirli karakterlerin varlığını kontrol etmek için artık SearchValues<char> kullanılabilir. SIMD talimatlarıyla donanım hızında çalışır.
// Önceki yöntem — her karakter için ayrı arama
private static readonly char[] InvalidChars = ['<', '>', '"', '\'', '&', '/', '\\'];
bool HasInvalidChar_Old(string input)
=> input.IndexOfAny(InvalidChars) >= 0;
// .NET 8 ile SearchValues
private static readonly SearchValues<char> _invalidChars =
SearchValues.Create(['<', '>', '"', '\'', '&', '/', '\\']);
bool HasInvalidChar_New(ReadOnlySpan<char> input)
=> input.ContainsAny(_invalidChars);
// Gerçek senaryo: URL sanitize
bool IsUrlSafe(string url)
{
ReadOnlySpan<char> span = url.AsSpan();
return !span.ContainsAny(_invalidChars);
}
// Byte dizilerinde de çalışır
private static readonly SearchValues<byte> _separators =
SearchValues.Create([(byte)'\r', (byte)'\n', (byte)'\t']);
int FindFirstSeparator(ReadOnlySpan<byte> data)
=> data.IndexOfAny(_separators);
4. TimeProvider: Test Edilebilir Zaman Soyutlaması
.NET 8'de zaman işlemleri için resmi bir soyutlama katmanı geldi. DateTime.UtcNow kullanmak yerine inject edilebilen bir servis artık mevcut.
// Önceki problem: DateTime.UtcNow test edilemezdi
public class TokenService
{
public bool IsExpired(Token token)
=> token.ExpiresAt < DateTime.UtcNow; // Test'te sabit zaman verilemez
}
// .NET 8 ile TimeProvider
public class TokenService(TimeProvider time)
{
public bool IsExpired(Token token)
=> token.ExpiresAt < time.GetUtcNow();
}
// Üretimde
builder.Services.AddSingleton(TimeProvider.System);
// Testte
var fakeTime = new FakeTimeProvider(new DateTimeOffset(2025, 6, 1, 0, 0, 0, TimeSpan.Zero));
var sut = new TokenService(fakeTime);
// Timer'lar da TimeProvider üzerinden çalışır
var timer = time.CreateTimer(
callback: _ => Console.WriteLine("Tick"),
state: null,
dueTime: TimeSpan.FromSeconds(5),
period: TimeSpan.FromSeconds(5));
5. Keyed DI Servisleri
Aynı interface'in birden fazla implementasyonu için artık isimli (keyed) kayıt mümkün:
// Kayıt
builder.Services.AddKeyedSingleton<IStorageService, LocalStorage>("local");
builder.Services.AddKeyedSingleton<IStorageService, AzureBlobStorage>("azure");
builder.Services.AddKeyedSingleton<IStorageService, S3Storage>("s3");
// Injection
public class FileService(
[FromKeyedServices("azure")] IStorageService cloudStorage,
[FromKeyedServices("local")] IStorageService localStorage)
{
public async Task BackupAsync(string path)
{
var data = await localStorage.ReadAsync(path);
await cloudStorage.WriteAsync(path, data);
}
}
// Minimal API'de
app.MapPost("/upload", async (
IFormFile file,
[FromKeyedServices("azure")] IStorageService storage) =>
{
await storage.WriteAsync(file.FileName, file.OpenReadStream());
return Results.Ok();
});
6. GC: DATAS Modu
DOTNET_GCDynamicAdaptationMode=1 ortam değişkeni ile etkinleştirilen DATAS (Dynamic Adaptation To Application Sizes) modu, heap boyutunu iş yüküne göre dinamik olarak ayarlıyor.
Geleneksel Server GC'de heap, core sayısıyla doğru orantılı sabit bir boyuta ayarlanırdı. Bunu DATAS ile değiştirdiğinizde:
- Düşük yük dönemlerinde heap küçülüyor → OS'a bellek geri dönüyor
- Yüksek yük dönemlerinde heap büyüyor → GC baskısı azalıyor
- Konteyner ortamlarında bellek limitine daha iyi uyum sağlıyor
// runtimeconfig.json veya environment variable
{
"configProperties": {
"System.GC.DynamicAdaptationMode": 1
}
}
Benchmark Özeti
Resmi .NET 8 performans blog yazısından derlenen sayılar:
- JSON serialization (System.Text.Json): ~%25 daha hızlı
- Regex (compiled): ~%30 daha hızlı
- LINQ Select+Where: ~%15 daha hızlı
- HTTP/2 throughput: ~%20 artış
- Startup (Native AOT): ~%50 daha hızlı
.NET 8, LTS sürümü olduğu için 2026'ya kadar güvenlik ve bakım güncellemeleri alacak. Eğer hâlâ .NET 6'da veya daha eskisindeyseniz, performans kazanımları tek başına yükseltme için yeterli bir neden.