• EmitMapper,AutoMapper,NLiteMapper和手工映射性能大比拼


      在大比拼之前先讲一个小插曲,我这个人以前比较低调,做了很多好东西仅仅在公司内的朋友圈项目圈内分享,很少在博客园内进行分享,后来在dudu 老大的文章博客园现代化建设——AutoMapper有感便推荐一下OOMapper 组件,于是乎接连写了几篇入门性的介绍使用文章:

       在园友Repository 兄的NLiteMapper与EmitMapper性能简单比较中了解到NLiteMapper与EmitMapper的性能巨大差距,于是乎进行了两天的性能优化,同时总结了优化过程:一次性能优化最佳实践。在这里非常感谢Repository 兄的测试,也非常感谢他把OOMapper纠正为NLiteMapper,否则NLiteMapper的性能是非常低下的,同时感谢dudu,感谢博客园给大家一个平台,在这个平台使我学到了很多很多......

         不说废话进入主题。

         准备工作:

    •   软硬件环境:VS2008,.net3.5, xp 双核
    •      测试组件(都是最新Release版本):AutoMapper.dll(v2.0), EmitMappe.dll (V1.0),NLite.dll(V1.0)

         性能测试工具:老赵的CodeTimer

         测试接口代码:

    [Contract]
    public interface IObjectToObjectMapper
    {
    //初始化映射器
    void Initialize();
    //执行映射
    void Map();
    }

         为了输出更友好的结果定义一下测试元数据代码:

    //测试映射器元数据
    public interface IMapperMetadata
    {
    //目录
    string Category { get; }
    //名称
    string Name { get; }
    string Descrption { get; }
    }

    //映射器元数据注解
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    [MetadataAttributeAttribute]
    public class MapperAttribute : ComponentAttribute
    {
    public string Category { get; set; }
    public string Name { get; set; }
    public string Descrption { get; set; }
    }

       利用NLite的Mini容器书写测试框架代码如下:测试次数10万次

    class Program
    {
    [InjectMany]
    private Lazy<IObjectToObjectMapper,IMapperMetadata>[] Mappers;

    //初始化映射器,并做一次映射操作
    void Init()
    {
    foreach (var item in Mappers)
    {
    item.Value.Initialize();
    item.Value.Map();
    }

    }

    //进行测试
    void Run()
    {
    foreach (var item in Mappers)
    CodeTimer.Time(item.Metadata.Category
    +"->" + item.Metadata.Name , 100000,() => item.Value.Map());
    }

    static void Main(string[] args)
    {
    ServiceRegistry.RegisteryFromAssemblyOf
    <Program>();

    var host
    = new Program();
    ServiceRegistry.Compose(host);

    host.Init();
    host.Run();

    Console.Read();
    }
    }

    这样完成了测试框架的搭建,现在就开始书写测试代码了。

      定义测试数据:

    public class ModelObject
    {
    public DateTime BaseDate { get; set; }
    public ModelSubObject Sub { get; set; }
    public ModelSubObject Sub2 { get; set; }
    public ModelSubObject SubWithExtraName { get; set; }
    }

    public class ModelSubObject
    {
    public string ProperName { get; set; }
    public ModelSubSubObject SubSub { get; set; }
    }

    public class ModelSubSubObject
    {
    public string IAmACoolProperty { get; set; }
    }

    public class ModelDto
    {
    public DateTime BaseDate { get; set; }
    public string SubProperName { get; set; }
    public string Sub2ProperName { get; set; }
    public string SubWithExtraNameProperName { get; set; }
    public string SubSubSubIAmACoolProperty { get; set; }
    }

        定义测试基类:

    public abstract class MapperBase : IObjectToObjectMapper
    {
    protected ModelObject _source;
    protected ModelDto _target;

    protected virtual void OnInitialize() { }
    public void Initialize()
    {
    OnInitialize();

    _source
    = new ModelObject
    {
    BaseDate
    = new DateTime(2007, 4, 5),
    Sub
    = new ModelSubObject
    {
    ProperName
    = "Some name",
    SubSub
    = new ModelSubSubObject
    {
    IAmACoolProperty
    = "Cool daddy-o"
    }
    },
    Sub2
    = new ModelSubObject
    {
    ProperName
    = "Sub 2 name"
    },
    SubWithExtraName
    = new ModelSubObject
    {
    ProperName
    = "Some other name"
    },
    };
    }

    public abstract void Map();
    }

           手工映射代码:

    [Mapper(Category = "Flattening.Class", Name = "Manual")]
    public class ManualMapper : MapperBase
    {
    public override void Map()
    {
    var destination
    = new ModelDto
    {
    BaseDate
    = _source.BaseDate,
    Sub2ProperName
    = _source.Sub2.ProperName,
    SubProperName
    = _source.Sub.ProperName,
    SubSubSubIAmACoolProperty
    = _source.Sub.SubSub.IAmACoolProperty,
    SubWithExtraNameProperName
    = _source.SubWithExtraName.ProperName
    };
    }
    }

          AutoMapper 映射代码:

    [Mapper(Category = "Flattening.Class", Name = "AutoMapper")]
    public class AutoMapperWrapper : MapperBase
    {
    protected override void OnInitialize()
    {
    AutoMapper.Mapper.Initialize(cfg
    =>
    {
    cfg.CreateMap
    <ModelObject, ModelDto>();
    });
    AutoMapper.Mapper.AssertConfigurationIsValid();
    }

    public override void Map()
    {
    _target
    =AutoMapper.Mapper.Map<ModelObject, ModelDto>(_source);
    }
    }

          EmitMapper映射代码:

    [Mapper(Category = "Flattening.Class", Name = "EmitMapper")]
    public class EmitMapperWrapper : MapperBase
    {
    ObjectsMapper
    <ModelObject, ModelDto> mapper;
    protected override void OnInitialize()
    {
    mapper
    = ObjectMapperManager.DefaultInstance.GetMapper<ModelObject, ModelDto>(new FlatteringConfig());
    }

    protected override ModelDto MapImp()
    {
    return mapper.Map(Source);
    }
    }

     EmitMapper映射器默认不支持Flatter 映射,如果支持需要写自定义配置:

    class FlatteringConfig : DefaultMapConfig
            {
                protected Func<string, string, bool> nestedMembersMatcher;
    
                public FlatteringConfig()
                {
                    nestedMembersMatcher = (m1, m2) => m1.StartsWith(m2);
                }
    
                public override IMappingOperation[] GetMappingOperations(Type from, Type to)
                {
                    var destinationMembers = GetDestinationMemebers(to);
                    var sourceMembers = GetSourceMemebers(from);
                    var result = new List<IMappingOperation>();
                    foreach (var dest in destinationMembers)
                    {
                        var matchedChain = GetMatchedChain(dest.Name, sourceMembers).ToArray();
                        if (matchedChain == null || matchedChain.Length == 0)
                        {
                            continue;
                        }
                        result.Add(
                            new ReadWriteSimple
                            {
                                Source = new MemberDescriptor(matchedChain),
                                Destination = new MemberDescriptor(new[] { dest })
                            }
                        );
                    }
                    return result.ToArray();
                }
    
                public DefaultMapConfig MatchNestedMembers(Func<string, string, bool> nestedMembersMatcher)
                {
                    this.nestedMembersMatcher = nestedMembersMatcher;
                    return this;
                }
    
                private List<MemberInfo> GetMatchedChain(string destName, List<MemberInfo> sourceMembers)
                {
                    var matches = sourceMembers.Where(s => MatchMembers(destName, s.Name) || nestedMembersMatcher(destName, s.Name));
                    int len = 0;
                    MemberInfo match = null;
                    foreach (var m in matches)
                    {
                        if (m.Name.Length > len)
                        {
                            len = m.Name.Length;
                            match = m;
                        }
                    }
                    if (match == null)
                    {
                        return null;
                    }
                    var result = new List<MemberInfo> { match };
                    if (!MatchMembers(destName, match.Name))
                    {
                        result.AddRange(
                            GetMatchedChain(destName.Substring(match.Name.Length), GetDestinationMemebers(match))
                        );
                    }
                    return result;
                }
    
                private static List<MemberInfo> GetSourceMemebers(Type t)
                {
                    return GetMemebers(t)
                        .Where(
                            m =>
                                m.MemberType == MemberTypes.Field ||
                                m.MemberType == MemberTypes.Property ||
                                m.MemberType == MemberTypes.Method
                        )
                        .ToList();
                }
    
                private static List<MemberInfo> GetDestinationMemebers(MemberInfo mi)
                {
                    Type t;
                    if (mi.MemberType == MemberTypes.Field)
                    {
                        t = mi.DeclaringType.GetField(mi.Name).FieldType;
                    }
                    else
                    {
                        t = mi.DeclaringType.GetProperty(mi.Name).PropertyType;
                    }
                    return GetDestinationMemebers(t);
                }
    
                private static List<MemberInfo> GetDestinationMemebers(Type t)
                {
                    return GetMemebers(t).Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property).ToList();
                }
    
                private static List<MemberInfo> GetMemebers(Type t)
                {
                    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
                    return t.GetMembers(bindingFlags).ToList();
                }
            }
    

              NLiteMapper映射代码:

    [Mapper(Category = "Flattening.Class", Name = "NLiteMapper")]
    public class NLiteMaperWrapper : MapperBase
    {
    private NLite.Mapping.IMapper<ModelObject, ModelDto> mapper;
    protected override void OnInitialize()
    {
    base.OnInitialize();

    mapper
    = NLite.Mapper.CreateMapper<ModelObject, ModelDto>();
    }

    public override void Map()
    {
    _target
    = mapper.Map(_source);
    }
    }

       Ok,完成代码用Release编译,然后再输出bin中找到exe文件,连续执行三次,下面是三次执行结果的截图:

    ------ Test started: Assembly: NLite.Test.dll ------
    
    Flattening.Class->AutoMapper
    	Time Elapsed:	1,112ms
    	CPU Cycles:	6,718,750
    	Gen 0: 		173
    	Gen 1: 		1
    	Gen 2: 		0
    
    Flattening.Class->NLiteMapper
    	Time Elapsed:	68ms
    	CPU Cycles:	781,250
    	Gen 0: 		4
    	Gen 1: 		1
    	Gen 2: 		0
    
    Flattening.Class->EmitMapper
    	Time Elapsed:	23ms
    	CPU Cycles:	156,250
    	Gen 0: 		3
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->Manual
    	Time Elapsed:	8ms
    	CPU Cycles:	0
    	Gen 0: 		3
    	Gen 1: 		1
    	Gen 2: 		0
    

    ------ Test started: Assembly: NLite.Test.dll ------
    
    Flattening.Class->AutoMapper
    	Time Elapsed:	1,701ms
    	CPU Cycles:	10,468,750
    	Gen 0: 		173
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->NLiteMapper
    	Time Elapsed:	69ms
    	CPU Cycles:	781,250
    	Gen 0: 		4
    	Gen 1: 		1
    	Gen 2: 		0
    
    Flattening.Class->EmitMapper
    	Time Elapsed:	22ms
    	CPU Cycles:	0
    	Gen 0: 		3
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->Manual
    	Time Elapsed:	10ms
    	CPU Cycles:	312,500
    	Gen 0: 		3
    	Gen 1: 		1
    	Gen 2: 		0
    
    
    1 passed, 0 failed, 0 skipped, took 2.98 seconds (NUnit 2.5.5).
    

    ------ Test started: Assembly: NLite.Test.dll ------
    
    Flattening.Class->AutoMapper
    	Time Elapsed:	1,205ms
    	CPU Cycles:	10,156,250
    	Gen 0: 		177
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->NLiteMapper
    	Time Elapsed:	66ms
    	CPU Cycles:	781,250
    	Gen 0: 		4
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->EmitMapper
    	Time Elapsed:	18ms
    	CPU Cycles:	312,500
    	Gen 0: 		3
    	Gen 1: 		0
    	Gen 2: 		0
    
    Flattening.Class->Manual
    	Time Elapsed:	9ms
    	CPU Cycles:	0
    	Gen 0: 		3
    	Gen 1: 		0
    	Gen 2: 		0
    
    
    1 passed, 0 failed, 0 skipped, took 2.56 seconds (NUnit 2.5.5).
    

    通过测试结果可以看出:

    •          手工映射速度最快
    •            EmitMapper第二(大约比手工慢了2-6倍,)
    •            NLiteMapper第三(大约比EmitMapper慢了3倍)
    •            最后是AutoMapper(大约比手工慢了200倍)

        内存开销结果:

    1. 手工映射              Gen 0: 3
    2. EmitMapper         Gen 0:3
    3. NLiteMapper        Gen 0: 4
    4. AutoMapper        Gen 0:173

        总结 :无论从性能和内存EmitMapper都接近于手工,NLiteMapper次之,AutoMapper最后。NLiteMapper,EmitMapper,AutoMapper都是通过Emit的方式进行Get和Set的,为什么性能差别如此之大,设想如果NLiteMapper不进行优化的话(NLiteMapper一直是通过Emit方式进行的),那么NLiteMapper肯定是高高垫背的(NLiteMapper比EmitMapper慢了15000倍)。。。。。。

      这次测试结果不代表整体结果,仅仅代表Class->Class(包括级联) 的映射性能,欢迎大家对这几种OO映射器进行性能比较。最后附上整个测试代码:测试代码

      备注:EmitMapper的测试代码修改过,添加了FlatteringConfig class 这样测试就公平了。

    附上的源代码是老代码,最新代码:http://nlite.codeplex.com/SourceControl/changeset/view/76359#1528885

  • 相关阅读:
    社群电商
    文字超出部分省略号显示······
    jq监听页面的滚动事件,
    input 更改 pleaseholder 的字体样式
    20161213 scrapy安装
    map按照value排序的方法
    Qt跨线程信号和槽的连接
    C++ map指针的使用
    Python 高级进阶
    [转]解读C指针(5)——笔试题解析
  • 原文地址:https://www.cnblogs.com/netcasewqs/p/2014684.html
Copyright © 2020-2023  润新知