Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Improve Performance of C# Object Mapping Code

How can I further improve the performance of the code below while still maintaining the public interface:

public interface IMapper<in TSource, in TDestination>
{
    void Map(TSource source, TDestination destination);
}

public static TDestination Map<TSource, TDestination>(
    this IMapper<TSource, TDestination> translator,
    TSource source)
    where TDestination : new()
{
    var destination = new TDestination();
    translator.Map(source, destination);
    return destination;
}

public static List<TDestination> MapList<TSource, TDestination>(
    this IMapper<TSource, TDestination> translator,
    List<TSource> source)
    where TDestination : new()
{
    var destinationCollection = new List<TDestination>(source.Count);
    foreach (var sourceItem in source)
    {
        var destinationItem = translator.Map(sourceItem);
        destinationCollection.Add(destinationItem);
    }
    return destinationCollection;
}

Example Usage

public class MapFrom { public string Property { get; set; } }

public class MapTo { public string Property { get; set; } }

public class Mapper : IMapper<MapFrom, MapTo>
{
    public void Map(MapFrom source, MapTo destination)
    {
        destination.Property = source.Property;
    }
}

var mapper = new Mapper();
var mapTo = mapper.Map(new MapFrom() { Property = "Foo" });
var mapToList = mapper.MapList(
    new List<MapFrom>() 
    {
        new MapFrom() { Property = "Foo" } 
    });

Current Benchmark

When I run a benchmark against a raw manual conversion, these are the numbers I get:

|             Method |  Job | Runtime |      Mean |     Error |    StdDev |       Min |       Max | Scaled | ScaledSD |  Gen 0 | Allocated |
|------------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|-------:|---------:|-------:|----------:|
|           Baseline |  Clr |     Clr |  1.969 us | 0.0354 us | 0.0332 us |  1.927 us |  2.027 us |   1.00 |     0.00 | 2.0523 |   6.31 KB |
|  Mapper            |  Clr |     Clr |  9.016 us | 0.1753 us | 0.2019 us |  8.545 us |  9.419 us |   4.58 |     0.12 | 2.0447 |   6.31 KB |
|           Baseline | Core |    Core |  1.820 us | 0.0346 us | 0.0355 us |  1.777 us |  1.902 us |   1.00 |     0.00 | 2.0542 |   6.31 KB |
|  Mapper            | Core |    Core |  9.043 us | 0.1725 us | 0.1613 us |  8.764 us |  9.294 us |   4.97 |     0.13 | 2.0447 |   6.31 KB |

Here is the code for the baseline:

var mapTo = new MapTo() { Property = mapFrom.Property };
var mapToCollection = new List<MapTo>(this.mapFrom.Count);
foreach (var item in this.mapFrom)
{
    destination.Add(new MapTo() { Property = item.Property });
}

Benchmark Code

I have a fully working project containing the mapper and Benchmark.NET project in the Dotnet-Boxed/Framework GitHub repository.

like image 250
Muhammad Rehan Saeed Avatar asked Sep 30 '17 07:09

Muhammad Rehan Saeed


People also ask

What is Optimisation in C?

Optimization is a program transformation technique, which tries to improve the code by making it consume less resources (i.e. CPU, Memory) and deliver high speed. In optimization, high-level general programming constructs are replaced by very efficient low-level programming codes.

Why does C run so fast?

But to answer your question, well-written C code will generally run faster than well-written code in other languages because part of writing C code "well" includes doing manual optimizations at a near-machine level.

Does C or C++ run faster?

Performance-based on Nature Of Language C++ language is an object-oriented programming language, and it supports some important features like Polymorphism, Abstract Data Types, Encapsulation, etc. Since it supports object-orientation, speed is faster compared to the C language.


1 Answers

After implementing the suggestions discussed in the comments, here is the most efficient MapList<TSource, TDestination> implementation I've been able to come up with:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

public static List<TDestination> MapList<TSource, TDestination>(
    this IMapper<TSource, TDestination> translator,
    List<TSource> source)
    where TDestination : new()
{
    var destinationCollection = new List<TDestination>(source.Count);

    foreach (var sourceItem in source)
    {
        TDestination dest = Factory<TDestination>.Instance();
        translator.Map(sourceItem, dest);
        destinationCollection.Add(dest);
    }

    return destinationCollection;
}

static class Factory<T>
{
    // Cached "return new T()" delegate.
    internal static readonly Func<T> Instance = CreateFactory();

    private static Func<T> CreateFactory()
    {
        NewExpression newExpr = Expression.New(typeof(T));

        return Expression
            .Lambda<Func<T>>(newExpr)
            .Compile();
    }
}

Note that I managed to take advantage of Jon Skeet's suggestion not to use new TDestination() without requiring the caller to provide the Func<TDestination> delegate, thus preserving your API.

Of course, the cost of compiling the factory delegate is non-negligible, but in common mapping scenarios I expect it to be worth the trouble.

like image 99
Kirill Shlenskiy Avatar answered Oct 19 '22 18:10

Kirill Shlenskiy