Created
October 12, 2016 17:43
-
-
Save AlbertoMonteiro/e1d18f41295cf4d59711bb4d985d9903 to your computer and use it in GitHub Desktop.
Object Mapper Benchmark
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using AutoMapper; | |
namespace AutoMapperBenchMark | |
{ | |
public sealed class PessoaProfile : Profile | |
{ | |
public PessoaProfile() | |
{ | |
CreateMap<Pessoa, PessoaViewModel>(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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