5

我是 AutoMapper 的忠实粉丝。我现在在许多项目中使用它来映射不同域之间的实体,例如从 wcf 服务模型到业务模型。

在一个示例网站中进行了一些负载测试(使用 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 毫秒,而手动映射需要不到 1 毫秒。我“仅”映射 500 个“简单”对象!也许对于 MVC ViewModel,这种情况很少发生,但其他映射它可能会很频繁。

30 毫秒似乎很快,但真正的麻烦是这 30 毫秒(白费)是Web 服务器上的 CPU 时间 服务器如何处理重负载(100 个并发用户)?事实上不好,这就是我们的负载测试引发警告的原因。

我创建了一种使用 Mapper.CreateMapExpression 生成 linq 表达式的方法,但不幸的是,该表达式不包含嵌套类型或选项。

那么,有没有什么办法可以提高 AutoMapper 的性能呢?有最佳实践吗?

谢谢,

4

1 回答 1

13

查看本文以了解有关 AutoMapper、其替代品以及性能指标的一些见解。我认为它会给你更全面的观点,你不会那么关心性能。

哪个更快:AutoMapper、Valuinjector 或手动映射?每个速度快到什么程度?

我的意见将是相似的 - 您的问题没有正确答案(您在帖子中并没有真正提出问题:))。您在 CPU 时间与人工时间(维护)之间取得平衡,您无法将两者进行比较。但是这个恕我直言应该是决定性因素,但显然取决于您采取什么方法。

编辑

试试看这里,看看你是否找到了一些与你的情况相关的线索:需要加速自动映射器……做 113 个对象需要 32 秒

还有另一个链接(虽然从 2009 年开始),但可能仍然相关...... 分析 AutoMapper 性能

谢谢,希望这会有所帮助。

于 2012-09-21T14:47:42.713 回答