Skip to content

Instantly share code, notes, and snippets.

@AlbertoMonteiro
Created October 12, 2016 17:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlbertoMonteiro/e1d18f41295cf4d59711bb4d985d9903 to your computer and use it in GitHub Desktop.
Save AlbertoMonteiro/e1d18f41295cf4d59711bb4d985d9903 to your computer and use it in GitHub Desktop.
Object Mapper Benchmark
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;
using AutoMapper;
using BenchmarkDotNet.Attributes;
using ExpressMapper.Extensions;
namespace AutoMapperBenchMark
{
public class ConversionTest
{
private readonly List<Pessoa> _pessoas;
private readonly Func<Pessoa, PessoaViewModel> _pessoaMapper;
private readonly Func<Pessoa, PessoaViewModel> _pessoaMapperWithIl;
private readonly Func<Pessoa, PessoaViewModel> _pessoaDoMap;
public ConversionTest()
{
Mapper.Initialize(c => c.AddProfiles(typeof(Program).Assembly));
ExpressMapper.Mapper.Register<Pessoa, PessoaViewModel>();
ExpressMapper.Mapper.Compile();
_pessoaMapper = CreateMap<Pessoa, PessoaViewModel>();
_pessoaMapperWithIl = CreateMapWithIL<Pessoa, PessoaViewModel>();
_pessoaDoMap = pessoa =>
{
var map = new PessoaViewModel();
map.Casado = pessoa.Casado;
map.DataNascimento = pessoa.DataNascimento;
map.Id = pessoa.Id;
map.Idade = pessoa.Idade;
map.Nome = pessoa.Nome;
return map;
};
_pessoas = Enumerable.Range(1, 100)
.Select(x => new Pessoa(x, $"Pessoa {x}", x + 10, DateTime.Today.AddYears(x), x % 2 == 0))
.ToList();
}
[Benchmark]
public void SelfCoding()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
pessoasVM.Add(new PessoaViewModel
{
Casado = pessoa.Casado,
DataNascimento = pessoa.DataNascimento,
Id = pessoa.Id,
Idade = pessoa.Idade,
Nome = pessoa.Nome
});
}
[Benchmark]
public void WithCustomExpressionEmitMapper()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
pessoasVM.Add(_pessoaMapper(pessoa));
}
[Benchmark]
public void WithAutoMapper()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
pessoasVM.Add(Mapper.Instance.Map<PessoaViewModel>(pessoa));
}
[Benchmark]
public void WithExpressMapper()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
pessoasVM.Add(pessoa.Map<Pessoa, PessoaViewModel>());
}
[Benchmark]
public void WithoutUsingMethod()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
{
var map = new PessoaViewModel();
map.Casado = pessoa.Casado;
map.DataNascimento = pessoa.DataNascimento;
map.Id = pessoa.Id;
map.Idade = pessoa.Idade;
map.Nome = pessoa.Nome;
pessoasVM.Add(map);
}
}
[Benchmark]
public void WithCustomILEmitMapper()
{
var pessoasVM = new List<PessoaViewModel>();
foreach (var pessoa in _pessoas)
pessoasVM.Add(_pessoaMapperWithIl.Invoke(pessoa));
}
public static Func<TSource, TDestination> CreateMapWithIL<TSource, TDestination>()
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var dynamicMethod = new DynamicMethod("Converter", destinationType, new[] { sourceType });
var ilGenerator = dynamicMethod.GetILGenerator();
var constructorInfo = destinationType.GetConstructor(new Type[0]);
ilGenerator.Emit(OpCodes.Newobj, constructorInfo);
var propertiesToMap = sourceType.GetProperties()
.Concat(destinationType.GetProperties())
.GroupBy(p => p.Name)
.Where(g => g.Count() == 2)
.Select(g => g.Key)
.ToList();
foreach (var propName in propertiesToMap)
{
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, sourceType.GetMethod($"get_{propName}"));
ilGenerator.Emit(OpCodes.Call, destinationType.GetMethod($"set_{propName}"));
}
ilGenerator.Emit(OpCodes.Ret);
return (Func<TSource, TDestination>)dynamicMethod.CreateDelegate(typeof(Func<TSource, TDestination>));
}
public static Func<TSource, TDestination> CreateMap<TSource, TDestination>()
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var constructorInfo = destinationType.GetConstructor(new Type[0]);
var labelTarget = Expression.Label(destinationType);
var destinationVariable = Expression.Variable(destinationType, "destination");
var sourceExpression = Expression.Parameter(sourceType, "source");
var propertiesToMap = sourceType.GetProperties()
.Concat(destinationType.GetProperties())
.GroupBy(p => p.Name)
.Where(g => g.Count() == 2)
.Select(g => g.Key)
.ToList();
var expressions = propertiesToMap
.Select(pn => Expression.Assign(Expression.Property(destinationVariable, pn), Expression.Property(sourceExpression, pn)))
.Cast<Expression>()
.ToList();
expressions.Insert(0, Expression.Assign(destinationVariable, Expression.New(constructorInfo)));
expressions.Add(Expression.Return(labelTarget, destinationVariable));
expressions.Add(Expression.Label(labelTarget, Expression.Convert(Expression.Constant(default(TDestination)), typeof(TDestination))));
var blockExpression = Expression.Block(new[] { destinationVariable }, expressions);
var expression = Expression.Lambda<Func<TSource, TDestination>>(blockExpression, sourceExpression);
return expression.Compile();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="5.1.1" targetFramework="net461" />
<package id="BenchmarkDotNet" version="0.9.9" targetFramework="net461" />
<package id="BenchmarkDotNet.Core" version="0.9.9" targetFramework="net461" />
<package id="BenchmarkDotNet.Toolchains.Roslyn" version="0.9.9" targetFramework="net461" />
<package id="Expressmapper" version="1.8.3" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.Common" version="1.3.2" targetFramework="net461" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.3.2" targetFramework="net461" />
<package id="System.AppContext" version="4.1.0" targetFramework="net461" />
<package id="System.Collections" version="4.0.11" targetFramework="net461" />
<package id="System.Collections.Concurrent" version="4.0.12" targetFramework="net461" />
<package id="System.Collections.Immutable" version="1.2.0" targetFramework="net461" />
<package id="System.Console" version="4.0.0" targetFramework="net461" />
<package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="net461" />
<package id="System.Diagnostics.FileVersionInfo" version="4.0.0" targetFramework="net461" />
<package id="System.Diagnostics.StackTrace" version="4.0.1" targetFramework="net461" />
<package id="System.Diagnostics.Tools" version="4.0.1" targetFramework="net461" />
<package id="System.Dynamic.Runtime" version="4.0.11" targetFramework="net461" />
<package id="System.Globalization" version="4.0.11" targetFramework="net461" />
<package id="System.IO.FileSystem" version="4.0.1" targetFramework="net461" />
<package id="System.IO.FileSystem.Primitives" version="4.0.1" targetFramework="net461" />
<package id="System.Linq" version="4.1.0" targetFramework="net461" />
<package id="System.Linq.Expressions" version="4.1.0" targetFramework="net461" />
<package id="System.Reflection" version="4.1.0" targetFramework="net461" />
<package id="System.Reflection.Metadata" version="1.3.0" targetFramework="net461" />
<package id="System.Reflection.Primitives" version="4.0.1" targetFramework="net461" />
<package id="System.Resources.ResourceManager" version="4.0.1" targetFramework="net461" />
<package id="System.Runtime" version="4.1.0" targetFramework="net461" />
<package id="System.Runtime.Extensions" version="4.1.0" targetFramework="net461" />
<package id="System.Runtime.Handles" version="4.0.1" targetFramework="net461" />
<package id="System.Runtime.InteropServices" version="4.1.0" targetFramework="net461" />
<package id="System.Runtime.Numerics" version="4.0.1" targetFramework="net461" />
<package id="System.Security.Cryptography.Algorithms" version="4.2.0" targetFramework="net461" />
<package id="System.Security.Cryptography.Encoding" version="4.0.0" targetFramework="net461" />
<package id="System.Security.Cryptography.Primitives" version="4.0.0" targetFramework="net461" />
<package id="System.Security.Cryptography.X509Certificates" version="4.1.0" targetFramework="net461" />
<package id="System.Text.Encoding" version="4.0.11" targetFramework="net461" />
<package id="System.Text.Encoding.CodePages" version="4.0.1" targetFramework="net461" />
<package id="System.Text.Encoding.Extensions" version="4.0.11" targetFramework="net461" />
<package id="System.Threading" version="4.0.11" targetFramework="net461" />
<package id="System.Threading.Tasks" version="4.0.11" targetFramework="net461" />
<package id="System.Threading.Tasks.Extensions" version="4.0.0" targetFramework="net461" />
<package id="System.Threading.Tasks.Parallel" version="4.0.1" targetFramework="net461" />
<package id="System.Threading.Thread" version="4.0.0" targetFramework="net461" />
<package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="net461" />
<package id="System.Xml.XDocument" version="4.0.11" targetFramework="net461" />
<package id="System.Xml.XmlDocument" version="4.0.1" targetFramework="net461" />
<package id="System.Xml.XPath" version="4.0.1" targetFramework="net461" />
<package id="System.Xml.XPath.XDocument" version="4.0.1" targetFramework="net461" />
</packages>
using System;
namespace AutoMapperBenchMark
{
public class Pessoa
{
public Pessoa(long id, string nome, int idade, DateTime dataNascimento, bool casado)
{
Id = id;
Nome = nome;
Idade = idade;
DataNascimento = dataNascimento;
Casado = casado;
}
public long Id { get; }
public string Nome { get; }
public int Idade { get; }
public DateTime DataNascimento { get; }
public bool Casado { get; }
}
}
using AutoMapper;
namespace AutoMapperBenchMark
{
public sealed class PessoaProfile : Profile
{
public PessoaProfile()
{
CreateMap<Pessoa, PessoaViewModel>();
}
}
}
using System;
namespace AutoMapperBenchMark
{
public class PessoaViewModel
{
public long Id { get; set; }
public string Nome { get; set; }
public int Idade { get; set; }
public DateTime DataNascimento { get; set; }
public bool Casado { get; set; }
}
}
using BenchmarkDotNet.Running;
namespace AutoMapperBenchMark
{
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<ConversionTest>();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment