大家对ORM效率的争议多半在映射性能方面。自己的ORMLite也是如此,经过前段时间的折腾,已经找不出一个简单的方法再提升一下这部分的方法了。在此把优化涉及的几点记录一下。

注:用于性能测试的CodeTimer为赵劼先生所写。

注:有两个优化方法为hubro先生所提供,下面会指出。

一、反射的优化


简单的说,反射给了我们动态发现类型信息的能力,Type对象是开启反射之路的入口点。从Type对象那里,我们可以得到该类型的PropertyInfo、MethodInfo、ConstructorInfo等等。在ORM映射中,一般都会用到PropertyInfo。我们可以在PropertyInfo上访问(设置和获取统称为访问)对象的这个属性,但由于是动态发现信息再动态调用,所以性能和直接调用差了很多。我们用一个Person类测试一下:

    public class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

测试代码:

        public static void Test()
        {
            Person person = new Person();
            var type = person.GetType();
            //先取出来PropertyInfo对象,相当于缓存
            var idPropertyInfo = type.GetProperty("ID");
            var namePropertyInfo = type.GetProperty("Name");

            CodeTimer.Initialize();

            ;//一百万次
            CodeTimer.Time("直接访问", iteration, () =>
            {
                person.ID = ;
                person.Name = "loogn";
            });

            CodeTimer.Time("PropertyInfo访问", iteration, () =>
            {
                idPropertyInfo.SetValue(person, );
                namePropertyInfo.SetValue(person, "loogn");
            });
        }

测试结果:

直接访问
        Time Elapsed:   12ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

PropertyInfo访问
        Time Elapsed:   683ms
        CPU Cycles:     ,,,
        Gen :
        Gen :
        Gen :          

请按任意键继续. . .

上面循环了一百万次,尽管对PropertyInfo对象缓存了,调用效率差距还是很大的。后来在网上查询一番,测试一番,最后发现编译成强类型的委托调用起来是最好的(包括用IL生成同等的代码)。

测试代码:

            Person person = new Person();
            var type = person.GetType();
            //先得出来PropertyInfo对象,相当于缓存
            var idPropertyInfo = type.GetProperty("ID");
            var namePropertyInfo = type.GetProperty("Name");
            //这两个委托也是缓存起来的
            var idSetter = (Action<Person, int>)Delegate.CreateDelegate(typeof(Action<Person, int>), null, idPropertyInfo.GetSetMethod(true));
            var nameSetter = (Action<Person, string>)Delegate.CreateDelegate(typeof(Action<Person, string>), null, namePropertyInfo.GetSetMethod(true));

            CodeTimer.Initialize();

            ;//一百万次
            CodeTimer.Time("直接访问", iteration, () =>
            {
                person.ID = ;
                person.Name = "loogn";
            });

            CodeTimer.Time("强类型委托访问", iteration, () =>
            {
                idSetter(person, );
                nameSetter(person, "loogn");
            });

测试结果:

直接访问
        Time Elapsed:   11ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

强类型委托访问
        Time Elapsed:   18ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

请按任意键继续. . .

小伙伴儿是不是惊呆了!反正当时我是惊呆啦,效率直逼硬编码了。我是一个很乐观的人,看到这样的测试结果,就幸哉幸哉地以为优化之路到此结束!可是经过测试,和Dapper还差那么一点点,于是就闷闷不乐了.....,于是这篇文章还有下面的部分。

二、分支语句与多态


以前从来没有感觉ifelse会有什么效率问题,但是当分支很多的时候,用多态是一种效率更高而且更容易维护的方案。由于上面用了强类型的委托,赋值的时候就用判断字段的类型了。从数据库的类型对应到C#里,大概有十几种,加上可空类型,分支判断就会很多。这里提一下:判断一个对象的类型的时候尽量用is (比如 obj is int),经过测试,这样比比较Type效率要高。为了让读者明白我具体所指,贴出下面潦草的代码:(由于分支很少,下面代码测试结果并不明显)

    public abstract class Parent
    {
        public abstract void Do(object o);
    }
    public class Sub1 : Parent
    {
        public override void Do(object o)
        {
            var s = (int)o;
        }
    }
    public class Sub2 : Parent
    {
        public override void Do(object o)
        {
            var s = (string)o;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            CodeTimer.Initialize();
            ;

            Parent p1 = new Sub1();
            Parent p2 = new Sub2();

            CodeTimer.Time("Parse1", iteration, () =>
            {
                Parse1(p1, p2);
            });

            CodeTimer.Time("Parse2", iteration, () =>
            {
                Parse2();
            });
        }

        static void Parse1(Parent p1, Parent p2)
        {
            ;
            p1.Do(o1);
            ";
            p2.Do(o2);
        }

        static void Parse2()
        {
            ;
            if (o1 is int)
            {
                var s = (int)o1;
            }
            else if (o1 is string)
            {
                var s = (string)o1;
            }

            ";

            if (o2 is int)
            {
                var s = (int)o2;
            }
            else if (o2 is string)
            {
                var s = (string)o2;
            }
        }
    }

潦草的代码

经过这两部分的优化,效率基本和Dapper持平了,但是hubro先生执意不放:“为什么我们代码基本一样,怎么没你的效率高呢?”,于是有了接下来的两个优化。

三、实例化对象


Activator.CreateInstance给了我们一个很方便的实例化对象的方法。有Type参数和泛型参数的重载方法,如果我们可以用泛型,我们还可以加new()约束,用 T obj=new T()来实例化,但是经我测试,Activator.CreateInstance<T>()和 new T()的效率是一样的。为什么一样呢,用ILDASM查看一下IL代码,原来new T()生成的代码也是调用Activator.CreateInstance<T>()呀!!但是有一天hubro先生告诉了我一个更高效的方法:用表达式编译成强类型委托。

测试代码:

    class Program
    {
        static T NewT<T>() where T : new()
        {
            return new T();
        }
        static T CreateT<T>()
        {
            return Activator.CreateInstance<T>();
        }

        static void Main(string[] args)
        {
            CodeTimer.Initialize();
            ;
            var type = typeof(Person);
            var newFun = Expression.Lambda<Func<Person>>(Expression.New(type)).Compile();

            CodeTimer.Time("直接实例化", iteration, () =>
            {
                var p = new Person();
            });

            CodeTimer.Time("强类型委托", iteration, () =>
            {
                var p = newFun();
            });

            CodeTimer.Time("Activator非泛型", iteration, () =>
            {
                var p = Activator.CreateInstance(type);
            });

            CodeTimer.Time("Activator泛型", iteration, () =>
            {
                var p = CreateT<Person>();
            });

            CodeTimer.Time("new T()", iteration, () =>
            {
                var p = NewT<Person>();
            });
        }
    }

测试结果:

直接实例化
        Time Elapsed:   51ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

强类型委托
        Time Elapsed:   135ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

Activator非泛型
        Time Elapsed:   401ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

Activator泛型
        Time Elapsed:   496ms
        CPU Cycles:     ,,,
        Gen :
        Gen :
        Gen :          

new T()
        Time Elapsed:   484ms
        CPU Cycles:     ,,,
        Gen :
        Gen :
        Gen :          

请按任意键继续. . .

可以看出强类型委托实例化效率最接近直接实例化了,出乎意料的是非泛型的Activator.CreateInstance比泛型的还高一点,泛型版本和new T()前面说过了,是一样的。

四、类型转换


MySql数据库类型对应到C#类型的时候,发现有两个不太确定,bit不对应bool,tinyint不对应byte,所以需要自己处理一下。把一个对象转换成值类型用几种方法测试一下:

测试代码:

        static void Main(string[] args)
        {
            CodeTimer.Initialize();
            ;
            var type = typeof(Person);
            var newFun = Expression.Lambda<Func<Person>>(Expression.New(type)).Compile();

            ;

            CodeTimer.Time("拆箱转换", iteration, () =>
            {
                var a = (int)obj;
            });

            CodeTimer.Time("Convert转换", iteration, () =>
            {
                var a = Convert.ToInt32(obj);
            });

            CodeTimer.Time("Parse转换", iteration, () =>
            {
                var a = int.Parse(obj.ToString());
            });
        }

测试结果:

拆箱转换
        Time Elapsed:   28ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

Convert转换
        Time Elapsed:   68ms
        CPU Cycles:     ,,
        Gen :
        Gen :
        Gen :          

Parse转换
        Time Elapsed:   ,141ms
        CPU Cycles:     ,,,
        Gen :
        Gen :
        Gen :          

请按任意键继续. . .

所以在转换bool值的时候,可以这样

if (obj is bool)
{
    var b = (bool)obj;
}
else
{
    ;
}

经过这些优化,映射效率已经超过Dapper了。但是又有一天hubro先生给我截图,说他的Mapping已经超过我了,我一下子不淡定了,这怎么可能,我从来都是写一些简单的明显没有效率问题的代码,所以赶紧请教(就是下面要说的)!

五、DbDataReader的GetValue和GetValues


在从DataReader取值的时候,hubro发现用GetValues一下子取出来,比在循环中一个一个取要快,测试了一下果真如此:

        public static List<T> ReaderToObjectList<T>(DbDataReader reader)
        {
            if (!reader.HasRows)
            {
                return new List<T>();
            }
            var refInfo = ReflectionHelper.GetInfo<T>();
            List<T> list = new List<T>();
            var first = true;
            int length = reader.FieldCount;
            ReflectionInfo<T>.Accessor[] accessorArray = new ReflectionInfo<T>.Accessor[length];
            object[] values = new object[length];
            while (reader.Read())
            {
                reader.GetValues(values);
                T obj = refInfo.NewInstance();// Activator.CreateInstance<T>();
                if (first)
                {
                    ; i < length; i++)
                    {
                        var fieldName = reader.GetName(i);
                        var accessor = refInfo.GetAccessor(fieldName);
                        accessorArray[i] = accessor;

                        accessor.Set(obj, values[i]);
                    }
                    first = false;
                }
                else
                {
                    ; i < length; i++)
                    {
                        accessorArray[i].Set(obj, values[i]);
                    }
                }
                list.Add(obj);
            }
            return list;
        }

至此,我们的效率已经甩开Dapper了!

一个大写的

Loogn.OrmLite映射优化记录的更多相关文章

  1. 谈谈我的入门级实体框架Loogn.OrmLite

    每次看到有新的ORM的时候,我总会留意一下,因为自己也写过一个这样的框架,人总是有比较之心的.我可能会down下来跑一跑,也可能不会这么做,这个取决于跑起来的难易程度.我是很懒的,有XML配置或其他稍 ...

  2. VS2010/2012配置优化记录笔记

    VS2010/2012配置优化记录笔记 在某些情况下VS2010/2012运行真的实在是太卡了,有什么办法可以提高速度吗?下面介绍几个优化策略,感兴趣的朋友可以参考下,希望可以帮助到你   有的时候V ...

  3. React性能优化记录(不定期更新)

    React性能优化记录(不定期更新) 1. 使用PureComponent代替Component 在新建组件的时候需要继承Component会用到以下代码 import React,{Componen ...

  4. Loogn.OrmLite文档

    Getting Started 一. 引入Loogn.OrmLite PM> Install-Package Loogn.OrmLite 二.引入名称空间 using Loogn.OrmLite ...

  5. # log对数Hash映射优化

    log对数Hash映射优化 利用了一个数学技巧:$\forall k \in [0,35],2^{k} mod 37 互不相等,且恰好取遍整数1-36 $ 应用:将int范围内的\(2^k映射到k\) ...

  6. 10w行级别数据的Excel导入优化记录

    需求说明 项目中有一个 Excel 导入的需求:缴费记录导入 由实施 / 用户 将别的系统的数据填入我们系统中的 Excel 模板,应用将文件内容读取.校对.转换之后产生欠费数据.票据.票据详情并存储 ...

  7. 我的搜索优化记录(一):中文分词优化IK Analyzer

    搜索绝对不仅仅是搭起框架,跑出结果就完成的工作,之后分词.排序等等的优化才是重头戏. 先交代下背景:这个搜索是我一个人负责搭建并优化的项目,主要索引对象为歌曲.歌手MV等等. 使用技术:Lucene. ...

  8. ElasticSearch CPU和内存占用高的优化记录

    公司最近使用ElasticSearch作为数据报表汇总引擎.上线三个月累计数据800万,但是今天突然大面积出现查询超时,上服务器查看服务运行情况,发现cpu使用率高达300% mem 使用率也到了90 ...

  9. mysql参数优化记录

    服务器参数16G内存,4核CPUvim /etc/my.cnf 原: back_log=170 max_connections=600 max_user_connections=0 thread_co ...

随机推荐

  1. iOS UISearchController的使用

    在iOS9中,UISearchDisplayController 已经被UISearchController替代.搜索框是一种常用的控件. 假设我们要满足下图的需求,产生100个“数字+三个随机字母” ...

  2. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q121-Q123)

    Question  121 You are designing a SharePoint 2010 workflow that will be used to monitor invoices. Th ...

  3. iOS 开发中的争议(一)

    序言 打算分享一些有争议的话题,并且表达一下我的看法.这是该系列的第一篇,我想讨论的是:类的成员变量应该如何定义? 在 Objective-C 的语言的早期,类的私有成员变量是只能定义在 .h 的头文 ...

  4. 浅谈html5 响应式布局

    一.什么是响应式布局? 响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本. 这个概念是为解决移动互联 ...

  5. (一)Maven初步了解与认识

    Apache Maven是一个软件项目管理的综合工具.基于项目对象模型(POM)的概念,提供了帮助管理构建.文档.报告.依赖.发布等方法,Maven简化和标准化项目建设过程.处理编译,分配,文档,团队 ...

  6. #研发解决方案#基于Apriori算法的Nginx+Lua+ELK异常流量拦截方案

    郑昀 基于杨海波的设计文档 创建于2015/8/13 最后更新于2015/8/25 关键词:异常流量.rate limiting.Nginx.Apriori.频繁项集.先验算法.Lua.ELK 本文档 ...

  7. SQL SERVER 2012启动失败 because upgrade step 'SSIS_hotfix_install.sql' 失败

    有台数据库服务器(开发服务器),开发人员邮件告诉我,SSMS连接不了这台服务器,远程登录后,发现SQL SERVER的服务停止了,启动服务时报错,服务启动不了.检查错误日志发现下面一些信息 2015- ...

  8. webForm(三)——三级联动

    三级联动 首先附图一张,初步认识一下什么是三级联动:                           注:选第一个后面两个变,选第二个,最后一个改变. 其次,做三级联动需要注意的方面:①DropD ...

  9. Lucene索引文件学习

     最近在做搜索,抽空看一下lucene,资料挺多的,不过大部分都是3.x了--在对着官方文档大概看一下. 优化后的lucene索引文件(4.9.0) 一.段文件 1.段文件:segments_5p和s ...

  10. Oracle索引梳理系列(七)- Oracle唯一索引、普通索引及约束的关系

    版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...