Hi there 🙂
Today is the Independence day in Belarus (hooray :)) and i have spare time to look at app performance a bit.
I’ve been lookin thru the code and found out that many of method that accept collection work with IEnumerable<T> for better flexibility. Sounds like good following of rule “Relax preconditions, strenghten postconditions”. But quite often i have a materialized collection with data(not yield-based generator) like List<T> or and array.
I found a place where i initially get string but then pass it as IEnumerable<string> and call string.Join over it.
Basically there are 5 overloads of string.Join (the second is actually generic string.Join<T>)
And i used the first one. Huh, quite interesting what is the difference between the one that works with array and the one that works with IE<string>.
First of all quite simple microbench:
(If u’re interested in those red lines under Console.WriteLine method – have a look at awesome performance fighting plugin for R# HeapView)
Simply changing .ToArray() to .ToList() changes the overload. Results are following:
Array – 73ms 81GC gen 0 collections
List – 115ms 202GC gen 0 collections
IEnumerable based implementation of string.Join simply creates StringBuilder and appends strings one by one.
Array based impl runs over provided array and calculates the total length of final string with separator, creates string with preallocated char buffer of appropriate length (using both internal and external FastAllocateString(int length) method) and then does unsafe(in terms of C#) memcpy to that preallocated buffer of every string from the array.
As for me, i’d use make my code a bit less nice in terms of preconditions and use arrays a bit more often. One more cool thing about using concrete collection types (T/List<T> instead of IEnumerable<T>/IList<T>) – they have struct enumerator, and iterating over them is faster (non-interface call) and do no heap allocations (passing struct as interface (IEnumerator) automatically boxes it, which is pretty expensive).
Have a nice day!