昨天jyk进群后,用Microsoft Application Center Test 对CYQ.Data 框架进行进行了一下压力测试
然后截了几张图上来,只有纯图如下:
1:使用了框架:sql 2000的分页存储过程[临时表分的页]:
![](http://static.oschina.net/uploads/img/201406/05233653_EXAT.jpg)
2:把存储过程直接换成select语句:
![](http://static.oschina.net/uploads/img/201406/05233653_USUU.jpg)
3:他的框架测试结果:
![](http://static.oschina.net/uploads/img/201406/05233653_d0nM.jpg)
4:这是测试结果了。
![](http://static.oschina.net/uploads/img/201406/05233653_J65E.jpg)
以下是说明:
1、DataTable :714次/秒2、MDataTable:559 次/秒 (简单存储过程)3、MDataTable:500 次/秒 (完整存储过程)
简单的说就是自定义的MDataTable性能不够理想,比DataTable还差,那还自定义干什么?直接使用DataTable不就了事了?
当然了,我当时的第一想法,MDataTable是不应该比DataTable慢的,算下体积,关联的类,都比DataTable简化这么多,怎么可能比DataTable性能差呢?
还记得我在发布: 时,在最下面的留言中,有一个测试
示例1:测试代码如下:
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
测试示例1
DateTime start = DateTime.Now; for ( int i = 0 ; i < 10000 ; i ++ ) { MDataTable table = new MDataTable( " myTableName " ); table.Columns.Add( " Url " , SqlDbType.NVarChar); table.Columns.Add( " Name " , SqlDbType.NVarChar); MDataRow mdr = table.NewRow(); mdr[ 0 ].Value = " http://cyq1162.cnblogs.com/ " ; mdr[ 1 ].Value = " 路过秋天 " ; table.Rows.Add(mdr); } TimeSpan ts = DateTime.Now - start; Response.Write( " MDataTable: " + ts.Ticks.ToString() + " <br> " ); DateTime start2 = DateTime.Now; for ( int j = 0 ; j < 10000 ; j ++ ) { DataTable table = new DataTable( " myTableName " ); table.Columns.Add( " Url " ); table.Columns.Add( " Name " ); DataRow mdr = table.NewRow(); mdr[ 0 ] = " http://cyq1162.cnblogs.com/ " ; mdr[ 1 ] = " 路过秋天 " ; table.Rows.Add(mdr); } TimeSpan ts2 = DateTime.Now - start2; Response.Write( " DataTable: " + ts2.Ticks.ToString());
得出的结果是:
1:MDataTable:1562502: DataTable:1562500结论: 自定义的MDataTable比DataTable速度快10倍。
不过就在回头间,我想到了,速度慢的原因可能在其实现的绑定机制上。
于是,我在上面的测试代码中,从界面拖了两个控件,分别为之绑定控件测试:
示例2:测试代码,看两句加红标注:
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
测试示例2
DateTime start = DateTime.Now; for ( int i = 0 ; i < 10000 ; i ++ ) { MDataTable table = new MDataTable( " myTableName " ); table.Columns.Add( " Url " , SqlDbType.NVarChar); table.Columns.Add( " Name " , SqlDbType.NVarChar); MDataRow mdr = table.NewRow(); mdr[ 0 ].Value = " http://cyq1162.cnblogs.com/ " ; mdr[ 1 ].Value = null ; table.Rows.Add(mdr); gv1.DataSource = table; gv1.DataBind(); } TimeSpan ts = DateTime.Now - start; Response.Write( " MDataTable: " + ts.Ticks.ToString() + " <br> " ); DateTime start2 = DateTime.Now; for ( int j = 0 ; j < 10000 ; j ++ ) { DataTable table = new DataTable( " myTableName " ); table.Columns.Add( " Url " ); table.Columns.Add( " Name " ); DataRow mdr = table.NewRow(); mdr[ 0 ] = " http://cyq1162.cnblogs.com/ " ; mdr[ 1 ] = null ; table.Rows.Add(mdr); gv2.DataSource = table; gv2.DataBind(); } TimeSpan ts2 = DateTime.Now - start2; Response.Write( " DataTable: " + ts2.Ticks.ToString());
得出的结果是:
1:MDataTable:179687502:DataTable: 10312500结论: 自定义的MDataTable在绑定时比DataTable慢了0.7倍左右。
虽然得之绑定时比DataTable慢,但具体慢的原因,还是得找出来的。
原因分析一:数据查询速度
第一步测试一下:返回一个MDataTable是不是比返回一个DataTable慢。
同时也怀疑是不是从SqlDataReader隐藏转换到MDataTable时,造成的性能差。
于是把框架简单修改一下,开放了SQLHelper,开放返回DataTable的方法,接着产生了以下的测试代码:
示例3:
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
测试示例3
string sql = " select * from Users " ; SQLHelper helper = new SQLHelper(); DateTime start2 = DateTime.Now; for ( int j = 0 ; j < 1000 ; j ++ ) { MDataTable mTable = helper.ExeDataReader(sql, false ); } TimeSpan ts2 = DateTime.Now - start2; Response.Write( " MDataTable: " + ts2.Ticks.ToString()); DateTime start = DateTime.Now; for ( int i = 0 ; i < 1000 ; i ++ ) { DataTable table = helper.ExeDataTable(sql, false ); } TimeSpan ts = DateTime.Now - start; Response.Write( " DataTable: " + ts.Ticks.ToString() + " <br> " ); helper.Dispose();
测试结果:
1:MDataTable:18750002: DataTable:2656250结论: 直接回返自定义的MDataTable比DataTable 快0.N倍。
从以上结果看出,无论在实例化,还是在查询速度上,自定义的MDataTable都是优于DataTable的。可是结果在绑定时反而变慢了,于是继续分析。
原因分析二:绑定机制
第一步,从实现绑定机制上走,首先自定义的MDataTable走的绑定机制源自DataReader方式,和DataTable不一样,于是产生第一个想法:
用源生的SqlDataReader绑定和DataTable绑定比较,测试代码:
示例4:
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
测试示例4
string sql = " select * from Users " ; DateTime start = DateTime.Now; for ( int i = 0 ; i < 1000 ; i ++ ) { SQLHelper helper = new SQLHelper(); DataTable table = helper.ExeDataTable(sql, false ); gv1.DataSource = table; gv1.DataBind(); helper.Dispose(); } TimeSpan ts = DateTime.Now - start; Response.Write( " DataTable: " + ts.Ticks.ToString() + " <br> " ); DateTime start2 = DateTime.Now; for ( int j = 0 ; j < 1000 ; j ++ ) { SQLHelper helper = new SQLHelper(); SqlDataReader reader = helper.ExeDataReader(sql, false ); gv2.DataSource = reader; gv2.DataBind(); reader.Close(); helper.Dispose(); } TimeSpan ts2 = DateTime.Now - start2; Response.Write( " SqlDataReader: " + ts2.Ticks.ToString());
测试结果:
1 : DataTable: 10156250 2 : SqlDataReader: 8437500 结论是: 用SqlDataReader绑定比DataTable绑定快一些。
于是,最后得出结论是:还真是我绑定代码写的有问题,导致性能比DataTable差了一点。
三:代码优化之章
既然代码写的不够好,就得优化了。于是接着研究DataReader的绑定接口的实现,发现有这么一些返回代码:
public override short GetInt16( int i) { this .ReadColumn(i); return this ._data[i].Int16; } public override int GetInt32( int i) { this .ReadColumn(i); return this ._data[i].Int32; } public override long GetInt64( int i) { this .ReadColumn(i); return this ._data[i].Int64; } 瞬间给了我一些启发,那就是模拟相似的实现方式了:
新建了一个类CellValueType:
并增加所有类型属性,一开始是prop的一个一个敲,累死人了。
internal class CellValueType{ public int Int; public string String; public bool Bool; public byte Byte; public char Char; public long Long; public DateTime DateTime; public decimal Decimal; public double Double; public float Float; public Type Type; public Guid Guid; public Int16 Int16; public Int32 Int32; public Int64 Int64; public short Short;}
接着增加方法,为属性设置值:手动敲这些代码,你说累不累人。
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
Set 方法
internal void Set( object value) { this .Type = value.GetType(); switch (Type.Name.ToLower()) { case " char " : this .Char = ( char )value; break ; case " boolean " : this .Bool = ( bool )value; break ; case " byte " : this .Byte = ( byte )value; break ; case " datetime " : this .DateTime = (DateTime)value; break ; case " decimal " : this .Decimal = ( decimal )value; break ; case " double " : this .Double = ( double )value; break ; case " guid " : this .Guid = (Guid)value; break ; case " int16 " : case " uint16 " : this .Int16 = (Int16)value; break ; case " int " : this .Int = ( int )value; break ; case " int32 " : case " uint32 " : this .Int32 = (Int32)value; break ; case " int64 " : case " uint64 " : this .Int64 = (Int64)value; break ; case " float " : case " single " : this .Float = ( float )value; break ; case " string " : this .String = ( string )value; break ; case " long " : this .Long = ( long )value; break ; case " short " : this .Short = ( short )value; break ; } }
接着在单元格类里增加该类,并在为单元格值赋值时调用此方法:
public class MDataCell{ // ...能省就省... internal CellValueType _CellValueType; // ...能省就省... #region 初始化 private void Init(CellStruct dataStruct, object value) { _CellValueType = new CellValueType(); // ...能省就省... } // ...能省就省... public object Value { get { return _CellValue.Value; } set { // ...能省就省... _CellValueType.Set(value); } }}
一切就绪,于是回到MDataTable实现接口的实现之处,写下和DataReader相似的代码:
public float GetFloat( int i) { return _Mdr[_Ptr][i]._CellValueType.Float; } public Guid GetGuid( int i) { return _Mdr[_Ptr][i]._CellValueType.Guid; } public short GetInt16( int i) { return _Mdr[_Ptr][i]._CellValueType.Short; }
改完之后,马上测试结果:
用的上面的示例2:
测试结果:
1 : MDataTable: 8906250 2 : DataTable: 11093750 结论: MDataTable在绑定时性能终于上去了,超越DataTable了
接着又用示例1:
测试结果:
1 :MDataTable: 312500 2 :DataTable: 1718750 结论是: 原来比DataTable快10倍的差距,纯减到5倍多一点。
按理就这么算了,绑定快一点,实例化时不要那么快,也是可以接受的。
不过,还是要抓个问底,究竟是哪句代码影响了性能。
于是继续研究,采取代码注释,步步换回原来的代码测试,终于把性能杀手抓出来了:
public string GetDataTypeName( int i) { return DataType.GetDbType(_Mdr[_Ptr][i]._CellStruct.SqlType.ToString()).ToString(); } public Type GetFieldType( int i) { return Type.GetType( " System. " + GetDataTypeName(i)); }
就是这两个家伙了,上面那个家伙还行,下面那个家伙就大大的不行了,Type.GetType方法,大伙自己拈着点用了。
既然抓到了真胸,那我原来的模仿存在的意义好像就不是那么明显了,只要能优化这里,那些模仿可以去掉了,同时又可以恢复原来10倍的性能差距。
当然了,同时我发现通过Value.GetType是有问题的,如果绑定的值是Null,虽然可以判断了事,不过每次对Value取值也不太稳。
于是新生方法又产生了:
为单元结构添加多一个属性,换掉整个CellValueType类。
在构造头部结构时,完成对Type的初始设置,如下:
/// <summary> /// 单元结构属性 /// </summary> public class CellStruct { // ...能省则省... internal Type ValueType; #region 构造函数 public CellStruct( string columnName, System.Data.SqlDbType sqlType, bool isReadOnly, bool isCanNull, int maxSize, ParameterDirection paraDirection) { // ...能省则省... ValueType = DataType.GetType(sqlType); } #endregion }
同时DataType增加一内部方法用于从SqlType转到Type:[又是手动敲的,累死人罗]
![05233653_xu8W.gif](http://static.oschina.net/uploads/img/201406/05233653_xu8W.gif)
GetType 方法
internal static Type GetType(SqlDbType sqlType) { switch (sqlType) { case SqlDbType.BigInt: return typeof (Int64); case SqlDbType.Binary: case SqlDbType.Image: case SqlDbType.Timestamp: case SqlDbType.VarBinary: return typeof (Byte); case SqlDbType.Bit: return typeof (Boolean); case SqlDbType.Text: case SqlDbType.Char: case SqlDbType.NChar: case SqlDbType.NText: case SqlDbType.NVarChar: case SqlDbType.VarChar: return typeof (String); case SqlDbType.SmallDateTime: case SqlDbType.DateTimeOffset: case SqlDbType.DateTime2: case SqlDbType.DateTime: case SqlDbType.Date: return typeof (DateTime); case SqlDbType.Money: case SqlDbType.Decimal: case SqlDbType.SmallMoney: return typeof (Decimal); case SqlDbType.Float: return typeof ( double ); case SqlDbType.Int: return typeof ( int ); case SqlDbType.Real: return typeof (Single); case SqlDbType.TinyInt: case SqlDbType.SmallInt: return typeof (Int16); case SqlDbType.UniqueIdentifier: return typeof (Guid); default : return typeof ( object ); } }
OK,至此,相关的返回取结构体的Type属性就行了,最后看测试结果:
示例1测试结果:
1:MDataTable:1562502:DataTable: 1718750 结论: 10倍的差距速度回来了。
对示例2测试结果:
1:MDataTable:87500002:DataTable:11093750 结论: 速度上仍超越了DataTable,虽然没超越多少。不过比起之前慢了0.7倍左右到现在反超回来,是一大提升了。
最后结案陈词:
继续欢迎光大群众对本框架的使用与测试,本框架将与时俱进,尽情做到让使用者放心,省心。欢迎留言讨论。