W poprzednim wpisie obiecywałem, porównać wydajność kolekcji immutable z klasycznymi typami z System.Collections.Generic. W dzisiejszym benchmarku, porównamy zarówno zużycie pamięci, jak i czas potrzebny na dodanie węzłów.
Zacznijmy od klasycznych (zmiennych) typów:
var list = new List<int>(); const int n = 100000; long beforeAvailableMemory = GC.GetTotalMemory(true); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { list.Add(i); } Console.WriteLine("Mutable, czas: {0}", stopwatch.ElapsedTicks); long afterAvailableMemory = GC.GetTotalMemory(false); Console.WriteLine("Mutable, pamiec: {0}", afterAvailableMemory - beforeAvailableMemory);
Wynik:
Następnie Microsoft immutable:
var immutableList = ImmutableList<int>.Empty; const int n = 100000; long beforeAvailableMemory = GC.GetTotalMemory(true); var stopwatch = Stopwatch.StartNew(); for (int i = 0; i < n; i++) { immutableList = immutableList.Add(i); } Console.WriteLine("Immutable, czas: {0}", stopwatch.ElapsedTicks); long afterAvailableMemory = GC.GetTotalMemory(false); Console.WriteLine("Immutable, pamiec: {0}", afterAvailableMemory - beforeAvailableMemory);
Wynik:
Różnica w pamięci jest tak naprawdę mniejsza niż można byłoby spodziewać się. Niestety czas wykonania jest wielokrotnie dłuższy. Oczywiście podany przykład zastosowania niezmiennych kolekcji, jest skrajnym anty-wzorcem, który pokazuje jak bardzo wydajność może spaść. W przyszłym wpisie, pokażę buildery, które umożliwiają tymczasową modyfikacje niezmiennych kolekcji – analogicznie do StringBuilder dla String.