Tuesday 14 March 2017

Object copying speed comparison, Part 2

      As planned - I'm adding another part of the overview of different methods used in the previous post. I'll try to dive into different nooks and crannies and see what I can learn from it. Before that I'd like to address questions raised.

1. It is unfair to compare converting done in parallel and the one done solely with AutoMapper. And unfair it is indeed. My goal however was to have an interesting learning experience. I wasn't planning to have a race.

2. Some of the cases don't make sense. And again it's correct. Most of them don't. Most of the time it's not the code that does objects conversion, which is our production code's bottleneck. Most of the time we'll find our performance issues elsewhere. Similarly as above - we're trying to have fun here and learn something new.

Going back to the samples:

  1. ManualMap - simple creation of the view models objects and copying of the data. First thing, which can increase the performance is setting the capacity of the output list to a predetermined size. Bear in mind this will become visible only with the increase of the items count.

                  

    The cause for this is memory reallocation and GC.
    Here's a good explanation: http://stackoverflow.com/questions/2247773/is-it-worthwhile-to-initialize-the-collection-size-of-a-listt-if-its-size-rea
  2. ManualMapFor - uses a for loop to iterate through the collection. In our case it was ~30%
    faster than the foreach version. Depending on the underlying collection the results may very. Iterating through arrays should be fastest due to the generated IL optimizations.
    Here's a good read on specifics:
    http://codebetter.com/patricksmacchia/2008/11/19/an-easy-and-efficient-way-to-improve-net-code-performances/
  3. AutoMapperMap - this case uses an AutoMapper library, which allows us to workaround the boilerplate code of copying the objects. It requires initial configuration - and it is slower, than using a manual copy adapter class. What we gain is the lack of all the extra boilerplate. Based on the results it was 10 times slower, than using manual adapter with a foreach loop.

    It would have been slightly faster, if we used a for loop over an array.
  4. AutoMapperCollectionMap - the mapping is always faster when we register a collection within the mapper. Performing a mapping of collection of objects to another collection of objects. Whenever we use Automapper that's the recommended way of performing the mapping.
  5. LinqMap - the most basic comparison to do was to compare it against an ordinary foreach loop. The LinqMap does a simple Select to do the mapping and ToList at the end. I expected it to be slower. This is how the comparison looks like:



    Select statement based on the collection it is running on, internally chooses an iterator, hence depending whether it's list or array we might get a speedup. Additionally Linq statements such as Select and Where use iterators, which allow for optimizing consecutive chained Where and Select calls. The logic behind them adds additional performance hit.

    To be continued...
General optimizations:
  1. Many .NET library methods perform optimizations based on type of collections we pass as a parameter. When we use the generic IEnumerable many will perform a check to see whether the collection implements ICollection, IList or whether it is an array. Knowing the more specific interface allows to use methods which make faster execution possible. Reads and writes themselves can differ in speed due to how out of bounds checks are performed in different cases. In the test below a foreach loop has performed manual copying over an array of dtos and a list of dtos. The array version is visibly faster.