Loogn.OrmLite映射优化记录
大家对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映射优化记录的更多相关文章
- 谈谈我的入门级实体框架Loogn.OrmLite
每次看到有新的ORM的时候,我总会留意一下,因为自己也写过一个这样的框架,人总是有比较之心的.我可能会down下来跑一跑,也可能不会这么做,这个取决于跑起来的难易程度.我是很懒的,有XML配置或其他稍 ...
- VS2010/2012配置优化记录笔记
VS2010/2012配置优化记录笔记 在某些情况下VS2010/2012运行真的实在是太卡了,有什么办法可以提高速度吗?下面介绍几个优化策略,感兴趣的朋友可以参考下,希望可以帮助到你 有的时候V ...
- React性能优化记录(不定期更新)
React性能优化记录(不定期更新) 1. 使用PureComponent代替Component 在新建组件的时候需要继承Component会用到以下代码 import React,{Componen ...
- Loogn.OrmLite文档
Getting Started 一. 引入Loogn.OrmLite PM> Install-Package Loogn.OrmLite 二.引入名称空间 using Loogn.OrmLite ...
- # log对数Hash映射优化
log对数Hash映射优化 利用了一个数学技巧:$\forall k \in [0,35],2^{k} mod 37 互不相等,且恰好取遍整数1-36 $ 应用:将int范围内的\(2^k映射到k\) ...
- 10w行级别数据的Excel导入优化记录
需求说明 项目中有一个 Excel 导入的需求:缴费记录导入 由实施 / 用户 将别的系统的数据填入我们系统中的 Excel 模板,应用将文件内容读取.校对.转换之后产生欠费数据.票据.票据详情并存储 ...
- 我的搜索优化记录(一):中文分词优化IK Analyzer
搜索绝对不仅仅是搭起框架,跑出结果就完成的工作,之后分词.排序等等的优化才是重头戏. 先交代下背景:这个搜索是我一个人负责搭建并优化的项目,主要索引对象为歌曲.歌手MV等等. 使用技术:Lucene. ...
- ElasticSearch CPU和内存占用高的优化记录
公司最近使用ElasticSearch作为数据报表汇总引擎.上线三个月累计数据800万,但是今天突然大面积出现查询超时,上服务器查看服务运行情况,发现cpu使用率高达300% mem 使用率也到了90 ...
- mysql参数优化记录
服务器参数16G内存,4核CPUvim /etc/my.cnf 原: back_log=170 max_connections=600 max_user_connections=0 thread_co ...
随机推荐
- git 提交解决冲突
一:git命令在提交代码前,没有pull拉最新的代码,因此再次提交出现了冲突. error: You have not concluded your merge (MERGE_HEAD exists) ...
- 更改pip源至国内镜像,显著提升下载速度(转载)
经常在使用Python的时候需要安装各种模块,而pip是很强大的模块安装工具,但是由于国外官方pypi经常被墙,导致不可用,所以我们最好是将自己使用的pip源更换一下,这样就能解决被墙导致的装不上库的 ...
- C# 多線程&BackgroundWorker概念入門教程
感謝以下各位作者的貢獻~ 百度經驗舉了個例子,很好理解BackgroundWorker的用途(主要是用來啟動後台線程,而不阻塞調用程式的運行),收藏一下 http://jingyan.baidu.c ...
- ListView实现点击事件以及总结
差点吓死我了,好不容易写的博客没有了,还好有自动保存功能,不然我真的是呜呜... ---恢复内容开始--- 开学一个月了,终于可以看见自己的作品雏形了. 从一个小白到现在半年了,觉得日子过得比较充实, ...
- Jquery——简单的视差滚动效果,兼容PC移动端
$(function(){ $(window).scroll(function(){ var top=$(this).scrollTop(); $(". ...
- IOS开发基础知识--碎片32
1:动画属性UIViewAnimationOptions说明 a:常规动画属性设置(可以同时选择多个进行设置) UIViewAnimationOptionLayoutSubviews:动画过程中保证子 ...
- iOS时间个性化设置设置
现在在很多项目中,不会直接显示时间,很多时候都是显示“刚刚”,”XX分钟前”,等等字样,那么他们是怎么实现的呢 ? .新建一个NSDate的类目:NSDate+XMGExtension NSDate+ ...
- ORA-01502: index ‘index_name' or partition of such index is in unusable state
错误现象: 今天发布脚本时,一个表插入数据时报如下错误 ORA-01502: index ‘index_name' or partition of such index is in unusable ...
- Android上传文件至服务器(上)
每一次都不能上首页,真悲催..管理员让我上一次首页? 很多时候我更愿意一个人写代码,与其在垃圾代码上改改改,我更愿意直接重构. 整洁的代码简单直接.整洁的代码如同优美的散文.整洁的代码从不隐藏设计者的 ...
- 对express中引入文件时提示Error: Cannot find module错误的理解
打算写个小demo,在引入一个routes文件时,一直提示Error: Cannot find module('./routes')的错误,经过一番了解. 如果要把整个文件夹下所有的模块都引进来 v ...