您的位置:首页 > 其它

CYQ.Data 轻量数据层之路 MDataTable 绑定性能优化之章(十一)

2012-03-09 00:00 671 查看
昨天jyk进群后,用Microsoft Application Center Test 对CYQ.Data 框架进行进行了一下压力测试

然后截了几张图上来,只有纯图如下:

1:使用了框架:sql 2000的分页存储过程[临时表分的页]:



2:把存储过程直接换成select语句:



3:他的框架测试结果:



4:这是测试结果了。



以下是说明:


1、DataTable :714次/秒
2、MDataTable:559 次/秒 (简单存储过程)
3、MDataTable:500 次/秒 (完整存储过程)

简单的说就是自定义的MDataTable性能不够理想,比DataTable还差,那还自定义干什么?直接使用DataTable不就了事了?

当然了,我当时的第一想法,MDataTable是不应该比DataTable慢的,算下体积,关联的类,都比DataTable简化这么多,怎么可能比DataTable性能差呢?

还记得我在发布:CYQ.Data 轻量数据层之路 自定义MDataTable绑定续章(七) 时,在最下面的留言中,有一个测试

示例1:测试代码如下:





测试示例1


DateTime start
=
DateTime.Now;

for
(
int
i
=

; 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[

].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
=

; j
<

10000
; j
++
)
{
DataTable table
=

new
DataTable(
"
myTableName
"
);
table.Columns.Add(
"
Url
"
);
table.Columns.Add(
"
Name
"
);

DataRow mdr
=
table.NewRow();
mdr[

]
=

" http://cyq1162.cnblogs.com/ "
;
mdr[
1
]
=

"
路过秋天
"
;
table.Rows.Add(mdr);
}
TimeSpan ts2
=
DateTime.Now
-
start2;
Response.Write(
"
DataTable:
"
+
ts2.Ticks.ToString());

得出的结果是:


1:MDataTable:156250
2: DataTable:1562500
结论:
自定义的MDataTable比DataTable速度快10倍。

不过就在回头间,我想到了,速度慢的原因可能在其实现的绑定机制上。

于是,我在上面的测试代码中,从界面拖了两个控件,分别为之绑定控件测试:

示例2:测试代码,看两句加红标注:





测试示例2


DateTime start
=
DateTime.Now;

for
(
int
i
=

; 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[

].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
=

; j
<

10000
; j
++
)
{
DataTable table
=

new
DataTable(
"
myTableName
"
);
table.Columns.Add(
"
Url
"
);
table.Columns.Add(
"
Name
"
);

DataRow mdr
=
table.NewRow();
mdr[

]
=

" 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:17968750
2:DataTable: 10312500
结论:
自定义的MDataTable在绑定时比DataTable慢了0.7倍左右。

虽然得之绑定时比DataTable慢,但具体慢的原因,还是得找出来的。

原因分析一:数据查询速度

第一步测试一下:返回一个MDataTable是不是比返回一个DataTable慢。

同时也怀疑是不是从SqlDataReader隐藏转换到MDataTable时,造成的性能差。

于是把框架简单修改一下,开放了SQLHelper,开放返回DataTable的方法,接着产生了以下的测试代码:

示例3:





测试示例3



string
sql
=

"
select * from Users
"
;
SQLHelper helper
=

new
SQLHelper();
DateTime start2
=
DateTime.Now;

for
(
int
j
=

; 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
=

; 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:1875000
2: DataTable:2656250
结论:
直接回返自定义的MDataTable比DataTable 快0.N倍。

从以上结果看出,无论在实例化,还是在查询速度上,自定义的MDataTable都是优于DataTable的。可是结果在绑定时反而变慢了,于是继续分析。

原因分析二:绑定机制

第一步,从实现绑定机制上走,首先自定义的MDataTable走的绑定机制源自DataReader方式,和DataTable不一样,于是产生第一个想法:

用源生的SqlDataReader绑定和DataTable绑定比较,测试代码:

示例4:





测试示例4



string
sql
=

"
select * from Users
"
;
DateTime start
=
DateTime.Now;

for
(
int
i
=

; 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
=

; 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;
}

接着增加方法,为属性设置值:手动敲这些代码,你说累不累人。





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:[又是手动敲的,累死人罗]





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:156250
2:DataTable: 1718750
结论:
10倍的差距速度回来了。

对示例2测试结果:


1:MDataTable:8750000
2:DataTable:11093750
结论:
速度上仍超越了DataTable,虽然没超越多少。不过比起之前慢了0.7倍左右到现在反超回来,是一大提升了。

最后结案陈词:


继续欢迎光大群众对本框架的使用与测试,本框架将与时俱进,尽情做到让使用者放心,省心。
欢迎留言讨论。

原文链接:
http://www.cnblogs.com/cyq1162/archive/2010/09/01/1814901.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐