Un moyen d'améliorer les Automapper la performance?

Je suis un grand fan de AutoMapper. Je suis maintenant de l'utiliser dans de nombreux projets pour la cartographie des entités entre les différents domaines de modèle de service wcf pour modèle d'affaires.

Après quelques tests de charge (avec VS Profiler) dans un échantillon de site web, j'ai trouvé que AutoMapper est responsable de la hausse de la consommation CPU.

J'ai fait une unité de ce comportement :

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace AutoMapper.Tests
{
[TestClass]
public class UnitTest
{
public class ClassSource
{
public string PropertyA { get; set; }
public int PropertyB { get; set; }
public NestedClassSource PropertyC { get; set; }
}
public class NestedClassSource
{
public string PropertyD { get; set; }
public DateTime PropertyE { get; set; }
public List<int> PropertyF { get; set; }
}
public class ClassDestination
{
public string PropertyA { get; set; }
public int PropertyB { get; set; }
public NestedClassDestination PropertyC { get; set; }
}
public class NestedClassDestination
{
public string PropertyD { get; set; }
public DateTime PropertyE { get; set; }
public List<int> PropertyF { get; set; }
}
[TestMethod]
public void MappingPerfTests()
{
Mapper.Initialize(a =>
{
a.CreateMap<ClassSource, ClassDestination>();
a.CreateMap<NestedClassSource, NestedClassDestination>();
});
Mapper.AssertConfigurationIsValid();
IList<ClassSource> items = GenerateRandomSources(nbItems: 500);
//automapper
MicroBench(() =>
{
var res = Mapper.Map<IList<ClassSource>, IList<ClassDestination>>(items);
}, nbIterations: 10);
//will take nearly 30 ms per test
//total : 300 ms
//manual mapper
MicroBench(() =>
{
var res = new List<ClassDestination>(items.Count);
foreach (var source in items)
{
res.Add(new ClassDestination()
{
PropertyA = source.PropertyA,
PropertyB = source.PropertyB,
PropertyC = new NestedClassDestination()
{
PropertyD = source.PropertyC.PropertyD,
PropertyE = source.PropertyC.PropertyE,
PropertyF = new List<int>(source.PropertyC.PropertyF)
}
});
}
}, nbIterations: 10);
//will take nearly 0 ms per test
//total : 1 ms
}
private IList<ClassSource> GenerateRandomSources(int nbItems = 1000)
{
IList<ClassSource> res = new List<ClassSource>(100);
foreach (var i in Enumerable.Range(1, nbItems))
{
ClassSource item = new ClassSource()
{
PropertyA = "PropertyA",
PropertyB = i,
PropertyC = new NestedClassSource() { PropertyD = "PropertyD", PropertyE = DateTime.Now, PropertyF = Enumerable.Range(1, 10).ToList() }
};
res.Add(item);
}
return res;
}
private void MicroBench(Action action, int nbIterations = 1000)
{
long totalElapsed = 0L;
foreach (var i in Enumerable.Range(1, nbIterations))
{
Stopwatch watcher = Stopwatch.StartNew();
action();
watcher.Stop();
Console.WriteLine("test : {0} ms", watcher.ElapsedMilliseconds);
totalElapsed += watcher.ElapsedMilliseconds;
}
Console.WriteLine("total : {0} ms", totalElapsed);
Console.WriteLine("avg : {0} ms", totalElapsed / nbIterations);
}
}
}

À la fin, AutoMapper semble assez lent : en moyenne 30 ms par test alors que le manuel de la cartographie prend moins d'une milliseconde. Je suis "seulement" cartographie 500 "simples" objets ! Peut-être pour un MVC ViewModel, cela se produit rarement, mais d'autres mappage il peut être fréquent.

30 ms semble être rapide, mais le vrai problème, c'est que ce 30 ms (pour rien) est temps CPU sur le serveur web Comment le serveur va traiter une charge lourde (100 utilisateurs simultanés) ? Pas bien en fait, c'est pourquoi notre test de charge, lance un avertissement.

J'ai fondé une façon de produire de l'expression linq avec Mappeur.CreateMapExpression, mais malheureusement, l'expression ne contient pas les types imbriqués ou des options.

Alors, est-il un moyen d'améliorer les AutoMapper la performance?
Existe-il des pratiques exemplaires?

Merci,

OriginalL'auteur Cybermaxs | 2012-09-21