重復造輪子感悟 – XLinq性能提升心得,xlinq性能提升
曾經的兩座大山
1、EF
剛接觸linq那段時間,感覺這家伙好神奇,語法好優美,好厲害。後來經歷了EF一些不如意的地方,就想去彌補,既然想彌補,就必須去了解原理。最開始甚至很長一段時間都搞不懂IQueryProvider(小聲說,其實現在也沒完全搞懂),好不容易把IQueryProvider搞懂了,然後才發現好戲才剛剛開始,這個時候嘗試寫了第一個ORM。那個時候不明白表達式樹的原理,自然一開始的思路就是走一點算一點,走到後面就沒法走了,因為思路太亂了。這個時候就感覺EF太牛了,那麼復雜的linq都能翻譯出來,雖然翻譯的sql的質量不行,而且還有坑,不過至少翻譯出來了,感覺自己永遠都沒辦法做到。
2、Dapper
這框架是大名鼎鼎的,性能是相當高的,底層是用EMIT寫的。然而我這人就是有技術潔癖,如果是我自己一個人開發,那麼如果這個工具讓我感覺到了不爽,我就會嘗試自己開發。所以我也沒用dapper,原因只是它直接操作了Connection,而且要手寫sql代碼。但是我自己寫的在性能上始終是個硬傷,跟dapper比不了。之前我業余用表達式樹實現了DataSet到List的轉換,然後拿到公司裝逼,同事來了一句"來來咱跟dapper比比",我說"得得你就別虐我了成不",比的結果當然是比較慘的,那個時候我覺得我不可能達到dapper的轉換速度。
性能提升測試
測試代碼
測試結果
秒EF是妥妥的,但dapper基本上性能一樣,那點差距就直接忽略吧。
之前也進行過一次,被dapper秒,和EF性能一樣,原因是因為字典緩存用的有問題。
而這次這個最終取數據全部用的數組,一開始其實把dapper都給秒了,快了dapper接近一半,不過後來發現情況沒考慮全,考慮全了就差不多了。
感悟和心得
IDataReader轉List關鍵代碼
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 Expression.Block(
47 assignIndexArrayVar,
48 Expression.Assign(
49 indexVar,
50 Expression.Add(indexVar, Expression.Constant(1))
51 )
52 ),
53 Expression.Break(forBreakLabel)
54 ),
55 forBreakLabel
56 ));
57 Expression body = null;
58 DataReaderGetMethodSwitcher switcher = null;
59 var labelTarget = Expression.Label(type, "return");
60 var paramterExpressions = new List<ParameterExpression>();
61 var setEntityExpressions = new List<Expression>();
62 if (TypeHelper.IsCompilerGenerated(type))
63 {
64 var constructor = type.GetConstructors().FirstOrDefault();
65 if (constructor == null)
66 {
67 throw new ArgumentException("類型" + type.FullName + "未找到構造方法");
68 }
69 var parameters = constructor.GetParameters();
70 var expressionParams = new List<ParameterExpression>();
71 for (int i = 0; i < fieldCount; i++)
72 {
73 var parameter = parameters[i];
74 var parameterVar = Expression.Variable(parameter.ParameterType, parameter.Name);
75 var parameterType = TypeHelper.GetUnderlyingType(parameter.ParameterType);
76 switcher = new DataReaderGetMethodSwitcher(parameterType, readIndexVar, readerVar);
77 switcher.Process();
78 var rightExp = (Expression)switcher.Result;
79 if (TypeHelper.IsNullableType(parameter.ParameterType))
80 {
81 rightExp = Expression.Convert(rightExp, parameter.ParameterType);
82 }
83 var isNullExp = Expression.Call(readerExp, ReflectorConsts.IsDBNullfIDataReader, readIndexVar);
84 var ifExp = Expression.IfThenElse(isNullExp, Expression.Assign(parameterVar, Expression.Default(parameter.ParameterType)), Expression.Assign(parameterVar, rightExp));
85 var exps = new List<Expression>();
86 setEntityExpressions.Add(
87 Expression.Assign(
88 readIndexVar,
89 Expression.ArrayIndex(indexArrVar, Expression.Constant(i))
90 )
91 );
92 setEntityExpressions.Add(ifExp);
93 expressionParams.Add(parameterVar);
94 }
95 setEntityExpressions.Add(Expression.Assign(objVar, Expression.New(constructor, expressionParams)));
96 paramterExpressions.AddRange(expressionParams);
97 paramterExpressions.Add(readerVar);
98 paramterExpressions.Add(listVar);
99 }
100 else
101 {
102 var newExp = Expression.New(type);
103 setEntityExpressions.Add(Expression.Assign(objVar, newExp));
104 for (int i = 0; i < fieldCount; i++)
105 {
106 var propertyName = reader.GetName(i);
107 var property = properties.Get(propertyName);
108 if (property == null)
109 {
110 continue;
111 }
112 var propertyAssignExpressions = new List<Expression>();
113 var propertyExp = Expression.Property(objVar, property);
114 var propertyType = TypeHelper.GetUnderlyingType(property.PropertyType);
115 Expression rightExp = null;
116 switcher = new DataReaderGetMethodSwitcher(propertyType, readIndexVar, readerVar);
117 switcher.Process();
118 rightExp = (Expression)switcher.Result;
119 if (TypeHelper.IsNullableType(property.PropertyType))
120 {
121 rightExp = Expression.Convert(rightExp, property.PropertyType);
122 }
123 setEntityExpressions.Add(
124 Expression.Assign(
125 readIndexVar,
126 Expression.ArrayIndex(indexArrVar, Expression.Constant(i))
127 )
128 );
129 var ifExp = Expression.IfThen(
130 Expression.Not(
131 Expression.Call(readerExp, ReflectorConsts.IsDBNullfIDataReader, readIndexVar)
132 ),
133 Expression.Assign(propertyExp, rightExp)
134 );
135 setEntityExpressions.Add(ifExp);
136 }
137 paramterExpressions.Add(listVar);
138 paramterExpressions.Add(readerVar);
139 }
140 paramterExpressions.Add(indexVar);
141 paramterExpressions.Add(propertyNameArr);
142 paramterExpressions.Add(fieldCountVar);
143 paramterExpressions.Add(indexArrVar);
144 paramterExpressions.Add(readIndexVar);
145 //expressions.Add(Expression.Call(
146 // null,
147 // typeof(MessageBox).GetMethod("Show", new Type[] { ReflectorConsts.StringType }),
148 // Expression.Call(
149 // null,
150 // ReflectorConsts.ConvertToStringMethod,
151 // Expression.Convert(
152 // Expression.ArrayIndex(indexArrVar, Expression.Constant(1)),
153 // ReflectorConsts.ObjectType)
154 // )));
155 setEntityExpressions.Add(Expression.Call(listVar, listType.GetMethods().FirstOrDefault(x => x.Name == "Add"), objVar));
156 expressions.Add(
157 Expression.Loop(
158 Expression.Block(
159 Expression.IfThenElse(
160 Expression.Call(readerVar, ReflectorConsts.ReadOfIDataReader),
161 Expression.Block(new[] { objVar }, setEntityExpressions),
162 Expression.Break(labelTarget, Expression.Default(type))
163 )
164 ),
165 labelTarget
166 ));
167 expressions.Add(listVar);
168 body = Expression.Block(
169 paramterExpressions,
170 expressions
171 );
172 func = Expression.Lambda<Func<IDataReader, IList>>(body, readerExp).Compile();
173 _dataReader2ListCahce.Add(type, func);
174 }
175 }
176 }
177 return func;
178 }
View Code