我是AutoMapper的忠实粉丝.我现在在许多项目中使用它来映射不同域之间的实体,例如从wcf服务模型到业务模型. 在示例网站中进行了一些负载测试(使用VS Profiler)之后,我发现AutoMapper负责高
在示例网站中进行了一些负载测试(使用VS Profiler)之后,我发现AutoMapper负责高CPU消耗.
我为这种行为做了一些单位:
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); } } }
最后,AutoMapper似乎很慢:每次测试平均30 ms,而手动映射只需不到1 ms.我“只”映射500个“简单”对象!也许对于MVC ViewModel来说,这种情况很少发生,但是映射它的其他人可能会经常发生.
30毫秒似乎很快,但真正的麻烦是这30毫秒(无用)是Web服务器上的CPU时间服务器如何处理重负载(100个并发用户)?实际上不太好,这就是为什么我们的负载测试会发出警告.
我创建了一种使用Mapper.CreateMapExpression生成linq表达式的方法,但不幸的是该表达式不包含嵌套类型或选项.
那么,有没有办法提高AutoMapper的性能?
有最佳做法吗?
谢谢,
阅读本文以获得有关AutoMapper,其替代品以及性能指标的一些见解.我认为它会给你更全面的看法,你也不会那么关心性能.Which is faster: AutoMapper, Valuinjector, or manual mapping? To what degree is each one faster?
我的观点是相似的 – 你的问题没有正确的答案(你在帖子中没有提出问题:)).你在CPU时间与人类时间(维持)之间取得平衡,你无法比较两者.但是这个恕我直言应该是决定性的因素,但它显然取决于你采取什么方法.
编辑
试着看这里,看看你是否找到了一些与你的情况相关的线索:Need to speed up automapper…It takes 32 seconds to do 113 objects
还有另一个链接(尽管从2009年开始),但也许相关…
Analyzing AutoMapper performance
谢谢,希望这会有所帮助.