Fork me on GitHub

[转载]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
        };
    }
}

      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>();
    }

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

          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文件,连续执行三次,下面是三次执行结果的截图:

  通过测试结果可以看出:

  • 手工映射速度最快
  • EmitMapper第三二(大约比手工慢了2倍,)
  • NLiteMapper第二(大约比手工慢了16倍,比EmitMapper慢了7-8倍)
  • 最后是AutoMapper(大约比手工慢了200倍,比EmitMapper慢了100多倍,比NLiteMapper慢了11倍多)

    内存开销结果:

  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映射器进行性能比较。最后附上整个测试代码:旧测试代码

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

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

文章出处:http://www.cnblogs.com/netcasewqs/archive/2011/04/13/2014684.html

原作者:风云

posted @ 2014-12-06 11:20  VAllen  阅读(1174)  评论(0编辑  收藏  举报