国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

重復(fù)造輪子感悟 – XLinq性能提升心得

2019-11-17 02:16:22
字體:
供稿:網(wǎng)友

重復(fù)造輪子感悟 – XLinq性能提升心得

曾經(jīng)的兩座大山

1、EF

剛接觸linq那段時(shí)間,感覺這家伙好神奇,語法好優(yōu)美,好厲害。后來經(jīng)歷了EF一些不如意的地方,就想去彌補(bǔ),既然想彌補(bǔ),就必須去了解原理。最開始甚至很長一段時(shí)間都搞不懂IQueryPRovider(小聲說,其實(shí)現(xiàn)在也沒完全搞懂),好不容易把IQueryProvider搞懂了,然后才發(fā)現(xiàn)好戲才剛剛開始,這個時(shí)候嘗試寫了第一個ORM。那個時(shí)候不明白表達(dá)式樹的原理,自然一開始的思路就是走一點(diǎn)算一點(diǎn),走到后面就沒法走了,因?yàn)樗悸诽珌y了。這個時(shí)候就感覺EF太牛了,那么復(fù)雜的linq都能翻譯出來,雖然翻譯的sql的質(zhì)量不行,而且還有坑,不過至少翻譯出來了,感覺自己永遠(yuǎn)都沒辦法做到。

2、Dapper

這框架是大名鼎鼎的,性能是相當(dāng)高的,底層是用EMIT寫的。然而我這人就是有技術(shù)潔癖,如果是我自己一個人開發(fā),那么如果這個工具讓我感覺到了不爽,我就會嘗試自己開發(fā)。所以我也沒用dapper,原因只是它直接操作了Connection,而且要手寫sql代碼。但是我自己寫的在性能上始終是個硬傷,跟dapper比不了。之前我業(yè)余用表達(dá)式樹實(shí)現(xiàn)了DataSet到List的轉(zhuǎn)換,然后拿到公司裝逼,同事來了一句"來來咱跟dapper比比",我說"得得你就別虐我了成不",比的結(jié)果當(dāng)然是比較慘的,那個時(shí)候我覺得我不可能達(dá)到dapper的轉(zhuǎn)換速度。

性能提升測試

測試代碼

  1. static void Main(string[] args)
  2. {
  3. EFDbContext db = new EFDbContext();
  4. db.Configuration.AutoDetectChangesEnabled = false;
  5. db.Configuration.LazyLoadingEnabled = false;
  6. db.Configuration.ValidateOnSaveEnabled = false;
  7. XLinqDataContext xlinq = new XLinqDataContext();
  8. db.Users.Where(x => false).ToList();//讓EF完成初始化
  9. ExecuteTimer("EF20萬數(shù)據(jù)查詢", () =>
  10. {
  11. db.LargUsers.Take(200000).ToList();
  12. });
  13. GC.Collect();
  14. ExecuteTimer("XLinq20萬數(shù)據(jù)查詢", () =>
  15. {
  16. var a = xlinq.Set<LargeUser>().Take(200000).ToList();
  17. });
  18. GC.Collect();
  19. ExecuteTimer("Dapper20萬數(shù)據(jù)查詢", () =>
  20. {
  21. SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString);
  22. var a = conn.Query<LargeUser>("SELECT top 200000 * from dbo.largeusers");
  23. });
  24. GC.Collect();
  25. ExecuteTimer("XLinq50萬數(shù)據(jù)查詢", () =>
  26. {
  27. var a = xlinq.Set<LargeUser>().Take(500000).ToList();
  28. });
  29. GC.Collect();
  30. ExecuteTimer("Dapper50萬數(shù)據(jù)查詢", () =>
  31. {
  32. SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["test"].ConnectionString);
  33. var a = conn.Query<LargeUser>("SELECT top 500000 * from dbo.largeUsers");
  34. });
  35. Console.ReadKey();
  36. }
  37. static void ExecuteTimer(string name, Action action)
  38. {
  39. var watch = Stopwatch.StartNew();
  40. action();
  41. watch.Stop();
  42. Console.WriteLine(string.Format("{0}:{1}毫秒", name, watch.ElapsedMilliseconds));
  43. }

測試結(jié)果

秒EF是妥妥的,但dapper基本上性能一樣,那點(diǎn)差距就直接忽略吧。

之前也進(jìn)行過一次,被dapper秒,和EF性能一樣,原因是因?yàn)樽值渚彺嬗玫挠袉栴}。

而這次這個最終取數(shù)據(jù)全部用的數(shù)組,一開始其實(shí)把dapper都給秒了,快了dapper接近一半,不過后來發(fā)現(xiàn)情況沒考慮全,考慮全了就差不多了。

感悟和心得

    1. Expression Tree與EMIT

      應(yīng)該有不少人都認(rèn)為后者比前者快,我一開始也這么認(rèn)為的。

      但園子一位大神說,Expression Tree與EMIT最終生成的代碼是一樣的,所以性能是一樣的。說實(shí)在的,我還是不太信。

      然后老趙大神又說,Expression Tree最終也是操作的EMIT來實(shí)現(xiàn)的。好吧,其實(shí)我有點(diǎn)信了,但還是不太信。

      直到我在寫XLinq的時(shí)候用純Expression Tree超越了用純EMIT的Dapper,我才相信。(不信?可以在評論里要源碼

    2. 基礎(chǔ)類型轉(zhuǎn)換的坑

      之前造輪子的時(shí)候,一直覺得Convert.ChangeType是尚方寶劍,一劍在手,天下我有。

      然而在寫XLinq的時(shí)候,被這個方法坑的不輕。

      當(dāng)我要long、int、short、int?、long?、short?這幾個類型相互轉(zhuǎn)換的時(shí)候,總會冒出來異常。

      然后谷歌一下,園子大神的博文說:"這個方法一遇到Nullable<T>類型的時(shí)候就會掛掉"。

      好吧,我認(rèn)了(說錯了的話請指正)。然后不得不自己處理類型轉(zhuǎn)換,同時(shí)也算是知道了這個坑。

    3. LINQ并不能完美實(shí)現(xiàn)無縫切換數(shù)據(jù)庫

      一直認(rèn)為只要linq provider寫得足夠好,就能支持無縫切換數(shù)據(jù)庫,注意是切換不是支持。

      然而現(xiàn)在看來,想要做到直接改一下配置文件就能切換這是不可能的。

      舉一個例子,假如說要將sql server數(shù)據(jù)庫切換到sqlite,這個時(shí)候切換過去之后看起來其實(shí)不會有什么問題,因?yàn)閟qlite即使語法不對也可能不會報(bào)錯。

      在sql server中,2015/1/1這是合法的日期型數(shù)據(jù),但這樣的數(shù)據(jù)在sqlite中是無法識別的。

      也就是說,如果你之前用的sql server,并且使用了這樣的數(shù)據(jù)格式,那么切換到sqlite后可能所有關(guān)于日期的判斷會全部出錯,并不是指報(bào)異常,而是說計(jì)算結(jié)果不正確。

    4. 將IDataReader轉(zhuǎn)換到List的功能整個直接生成代碼,不再生成每一個屬性的委托

      之前在寫類似于ORM的工具時(shí),總會將先生成每一個屬性的Setter和Getter委托,然后再單獨(dú)調(diào)用這些委托來完成轉(zhuǎn)換。

      這樣就幾乎無法避免裝箱拆箱的問題,但裝箱拆箱其實(shí)還是會浪費(fèi)一點(diǎn)性能的。

      后來想到了另一種辦法,不再生成Setter和Getter的委托,轉(zhuǎn)而生成包裝了一個循環(huán)的委托。

      就是說,我傳給委托一個類型和一個Reader對象,它就直接返回給我一個List,我不用去獲取每一個屬性的委托。

      委托中直接生成了訪問該類型的每一個屬性的代碼,這樣就直接避免了裝箱拆箱,最終性能跟dapper差不多了。

      不過這樣做可能會占用多一點(diǎn)緩存,不過現(xiàn)在來說內(nèi)存應(yīng)該不是問題

IDataReader轉(zhuǎn)List關(guān)鍵代碼

  1  public static Func<IDataReader, IList> GetDataReaderMapeer(Type type,IDataReader reader)  2         {  3             Func<IDataReader, IList> func = null;  4             if (!_dataReader2ListCahce.TryGetValue(type, out func))  5             {  6                 lock (_dataReader2ListCahce)  7                 {  8                     if (!_dataReader2ListCahce.TryGetValue(type, out func))  9                     { 10                         var readerExp = Expression.Parameter(ReflectorConsts.IDataReaderType, "reader"); 11                         var properties = ExpressionReflector.GetProperties(type); 12                         var fieldCount = reader.FieldCount; 13                         var expressions = new List<Expression>(); 14                         var objVar = Expression.Variable(type, "entity"); 15                         var fieldCountVar = Expression.Variable(ReflectorConsts.Int32Type, "fieldCount"); 16                         var readerVar = Expression.Variable(ReflectorConsts.IDataReaderType, "readerVar"); 17                         var propertyNameArr = Expression.Variable(ReflectorConsts.StringArrayType, "pis"); 18                         var indexArrVar = Expression.Variable(ReflectorConsts.Int32ArrayType, "indexes"); 19                         var readIndexVar = Expression.Variable(ReflectorConsts.Int32Type, "readIndex"); 20                         var indexVar = Expression.Variable(ReflectorConsts.Int32Type, "index"); 21                         var forBreakLabel = Expression.Label("forBreak"); 22                         var assignIndexVar = Expression.Assign(indexVar, Expression.Constant(0)); 23                         var listType = ReflectorConsts.ListType.MakeGenericType(type); 24                         var listVar = Expression.Variable(listType, "list"); 25                         expressions.Add(Expression.Assign(listVar, Expression.New(listType))); 26                         expressions.Add(Expression.Assign(readerVar, readerExp)); 27                         expressions.Add(assignIndexVar); 28                         var assignFieldCountVar = Expression.Assign(fieldCountVar, 29                                 Expression.MakeMemberaccess(readerVar, ReflectorConsts.FieldCountOfIDataReader) 30                             ); 31                         expressions.Add(assignFieldCountVar); 32                         var readNameExp = Expression.Call(readerVar, ReflectorConsts.GetOrdinalOfIDataReader, Expression.ArrayIndex(propertyNameArr, indexVar)); 33                         var initIndexArray = Expression.Assign(indexArrVar, Expression.NewArrayBounds(ReflectorConsts.Int32Type, Expression.Constant(fieldCount))); 34                         var initPropertyArrayExpressions = new List<Expression>(); 35                         for (int i = 0; i < fieldCount; i++) 36                         { 37                             initPropertyArrayExpressions.Add(Expression.Constant(reader.GetName(i))); 38                         } 39                         var initPropertyArray = Expression.Assign(propertyNameArr, Expression.NewArrayInit(ReflectorConsts.StringType, initPropertyArrayExpressions)); 40                         var assignIndexArrayVar = Expression.Assign(Expression.ArrayAccess(indexArrVar, indexVar), readNameExp); 41                         expressions.Add(initPropertyArray); 42                         expressions.Add(initIndexArray); 43                         expressions.Add(Expression.Loop( 44                                 Expression.IfThenElse( 45                                     Expression.LessThan(indexVar, fieldCountVar), 46
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 怀集县| 缙云县| 广南县| 和田市| 曲阜市| 江都市| 中宁县| 海林市| 奉贤区| 双牌县| 湛江市| 中西区| 中卫市| 革吉县| 富宁县| 会昌县| 漠河县| 松潘县| 临沭县| 固原市| 淮南市| 齐河县| 晋江市| 资阳市| 平定县| 乐陵市| 宜兴市| 北辰区| 体育| 马边| 武夷山市| 山东省| 龙口市| 高平市| 阿勒泰市| 昌邑市| 乃东县| 乳山市| 于都县| 福海县| 大田县|