5

私はAutoMapperの大ファンです。現在、wcfサービスモデルからビジネスモデルなど、さまざまなドメイン間でエンティティをマッピングするために多くのプロジェクトで使用しています。

サンプルWebサイトで(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時間と(維持するための)人間の時間のバランスを取り、2つを比較することはできません。しかし、このIMHOが決定的な要因になるはずですが、どのアプローチを取るかは明らかにあなた次第です。

編集

ここを見て、状況に関連する手がかりが見つかるかどうかを確認してください。オートマッパーを高速化する必要があります...113個のオブジェクトを実行するには32秒かかります

また別のリンク(2009年からですが)、しかしおそらくまだ関連しています... AutoMapperのパフォーマンスの分析

ありがとう、これがお役に立てば幸いです。

于 2012-09-21T14:47:42.713 に答える