Span ve Memory Optimizasyonu¶
Span
1. Substring ile Gereksiz Allocation¶
❌ Yanlış Kullanım: String.Substring ile yeni string nesneleri oluşturmak.
public string GetDomain(string email)
{
var atIndex = email.IndexOf('@');
return email.Substring(atIndex + 1); // Yeni string nesnesi oluşur
}
✅ İdeal Kullanım: Span ile allocation-free string işlemi yapın.
public ReadOnlySpan<char> GetDomain(ReadOnlySpan<char> email)
{
var atIndex = email.IndexOf('@');
return email[(atIndex + 1)..]; // Yeni nesne oluşmaz, mevcut belleğe referans
}
2. Array Kopyalama ile Allocation¶
❌ Yanlış Kullanım: Array.Copy ile yeni array oluşturmak.
public byte[] GetHeader(byte[] data)
{
var header = new byte[4];
Array.Copy(data, 0, header, 0, 4); // Yeni array allocation
return header;
}
✅ İdeal Kullanım: Span slice ile kopyalama olmadan erişin.
public ReadOnlySpan<byte> GetHeader(ReadOnlySpan<byte> data)
{
return data[..4]; // Allocation yok
}
3. String Concatenation ile Bellek İsrafı¶
❌ Yanlış Kullanım: Döngüde string birleştirme yapmak.
public string BuildCsv(IEnumerable<Product> products)
{
var result = "";
foreach (var product in products)
{
result += $"{product.Id},{product.Name},{product.Price}\n"; // Her iterasyonda yeni string
}
return result;
}
✅ İdeal Kullanım: StringBuilder veya string.Create kullanın.
public string BuildCsv(IEnumerable<Product> products)
{
var sb = new StringBuilder(1024);
foreach (var product in products)
{
sb.Append(product.Id).Append(',')
.Append(product.Name).Append(',')
.Append(product.Price).AppendLine();
}
return sb.ToString();
}
4. ArrayPool Kullanmamak¶
❌ Yanlış Kullanım: Her işlemde yeni byte array oluşturmak.
public async Task ProcessFileAsync(Stream stream)
{
var buffer = new byte[8192]; // Her çağrıda heap allocation
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
{
ProcessChunk(buffer, bytesRead);
}
}
✅ İdeal Kullanım: ArrayPool ile buffer’ları yeniden kullanın.
public async Task ProcessFileAsync(Stream stream)
{
var buffer = ArrayPool<byte>.Shared.Rent(8192);
try
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
{
ProcessChunk(buffer.AsSpan(0, bytesRead));
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
5. stackalloc Kullanmamak¶
❌ Yanlış Kullanım: Küçük geçici bufferlar için heap allocation.
public string FormatId(int id)
{
var bytes = new byte[16]; // Heap allocation
// Format işlemleri
return Convert.ToHexString(bytes);
}
✅ İdeal Kullanım: Küçük bufferlar için stackalloc ile stack üzerinde yer ayırın.
public string FormatId(int id)
{
Span<byte> bytes = stackalloc byte[16]; // Stack allocation, GC yükü yok
BitConverter.TryWriteBytes(bytes, id);
return Convert.ToHexString(bytes);
}
public bool TryParseDate(ReadOnlySpan<char> input, out DateTime result)
{
Span<char> buffer = stackalloc char[10];
// Stack üzerinde geçici çalışma alanı
return DateTime.TryParseExact(input, "yyyy-MM-dd", null,
System.Globalization.DateTimeStyles.None, out result);
}