zoukankan      html  css  js  c++  java
  • CYQ.Data 轻量数据层之路 MDataTable 绑定性能优化之章(十一)

    昨天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 = 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:156250
    2: DataTable:1562500
    结论:
    自定义的MDataTable比DataTable速度快10倍。

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

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

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

    测试示例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: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 = 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: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 = 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;
    }

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

    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倍左右到现在反超回来,是一大提升了。

    最后结案陈词:

    继续欢迎光大群众对本框架的使用与测试,本框架将与时俱进,尽情做到让使用者放心,省心。
    欢迎留言讨论。
    版权声明:本文原创发表于 博客园,作者为 路过秋天 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
    个人微信公众号
    创业QQ群:617713515
    Donation(扫码支持作者):支付宝:
    Donation(扫码支持作者):微信:
  • 相关阅读:
    iOS 即时通讯 + 仿微信聊天框架 + 源码
    Accelerate Framework in Swift
    最新 iOS 框架整体梳理(三)
    最新 iOS 框架整体梳理(二)
    单元测试
    iOS
    画个Shape留意到的东西
    deleteSections & deleteRows 我踩的坑
    常用开发技巧系列(六)
    程序员该如何提高睡眠质量?—程序媛233酱的助攻~
  • 原文地址:https://www.cnblogs.com/cyq1162/p/1814901.html
Copyright © 2011-2022 走看看