昨天jyk进群后,用Microsoft Application Center Test 对CYQ.Data 框架进行进行了一下压力测试
然后截了几张图上来,只有纯图如下:
1:使用了框架:sql 2000的分页存储过程[临时表分的页]:
2:把存储过程直接换成select语句:
3:他的框架测试结果:
4:这是测试结果了。
以下是说明:
2、MDataTable:559 次/秒 (简单存储过程)
3、MDataTable:500 次/秒 (完整存储过程)
简单的说就是自定义的MDataTable性能不够理想,比DataTable还差,那还自定义干什么?直接使用DataTable不就了事了?
当然了,我当时的第一想法,MDataTable是不应该比DataTable慢的,算下体积,关联的类,都比DataTable简化这么多,怎么可能比DataTable性能差呢?
还记得我在发布:CYQ.Data 轻量数据层之路 自定义MDataTable绑定续章(七) 时,在最下面的留言中,有一个测试
示例1:测试代码如下:
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());
得出的结果是:
2: DataTable:1562500
结论:自定义的MDataTable比DataTable速度快10倍。
不过就在回头间,我想到了,速度慢的原因可能在其实现的绑定机制上。
于是,我在上面的测试代码中,从界面拖了两个控件,分别为之绑定控件测试:
示例2:测试代码,看两句加红标注:
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
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());
得出的结果是:
2:DataTable: 10312500
结论:自定义的MDataTable在绑定时比DataTable慢了0.7倍左右。
虽然得之绑定时比DataTable慢,但具体慢的原因,还是得找出来的。
原因分析一:数据查询速度
第一步测试一下:返回一个MDataTable是不是比返回一个DataTable慢。
同时也怀疑是不是从SqlDataReader隐藏转换到MDataTable时,造成的性能差。
于是把框架简单修改一下,开放了SQLHelper,开放返回DataTable的方法,接着产生了以下的测试代码:
示例3:
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();
测试结果:
2: DataTable:2656250
结论:直接回返自定义的MDataTable比DataTable 快0.N倍。
从以上结果看出,无论在实例化,还是在查询速度上,自定义的MDataTable都是优于DataTable的。可是结果在绑定时反而变慢了,于是继续分析。
原因分析二:绑定机制
第一步,从实现绑定机制上走,首先自定义的MDataTable走的绑定机制源自DataReader方式,和DataTable不一样,于是产生第一个想法:
用源生的SqlDataReader绑定和DataTable绑定比较,测试代码:
示例4:
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());
测试结果:
2: SqlDataReader:8437500
结论是:用SqlDataReader绑定比DataTable绑定快一些。
于是,最后得出结论是:还真是我绑定代码写的有问题,导致性能比DataTable差了一点。
三:代码优化之章
既然代码写的不够好,就得优化了。于是接着研究DataReader的绑定接口的实现,发现有这么一些返回代码:
{
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的一个一个敲,累死人了。
{
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;
}
接着增加方法,为属性设置值:手动敲这些代码,你说累不累人。
{
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;
}
}
接着在单元格类里增加该类,并在为单元格值赋值时调用此方法:
{
//...能省就省...
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相似的代码:
{
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:
测试结果:
2: DataTable:11093750
结论:MDataTable在绑定时性能终于上去了,超越DataTable了
接着又用示例1:
测试结果:
2:DataTable:1718750
结论是:原来比DataTable快10倍的差距,纯减到5倍多一点。
按理就这么算了,绑定快一点,实例化时不要那么快,也是可以接受的。
不过,还是要抓个问底,究竟是哪句代码影响了性能。
于是继续研究,采取代码注释,步步换回原来的代码测试,终于把性能杀手抓出来了:
{
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>
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:[又是手动敲的,累死人罗]
{
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测试结果:
2:DataTable: 1718750
结论:10倍的差距速度回来了。
对示例2测试结果:
2:DataTable:11093750
结论:速度上仍超越了DataTable,虽然没超越多少。不过比起之前慢了0.7倍左右到现在反超回来,是一大提升了。
最后结案陈词:
欢迎留言讨论。