.Net 2.0: Entity as DTO vs Dataset as DTO / Xml Serialization vs JSON Serialization

本文以一组Entity vs Dataset的性能测试数据为基础,比较以Entity作为DTO和Dataset作为DTO的性能差异。测试可能不一定严密,但是一定程度上能够比较出优劣。希望能为您选择 .Net下不同的数据承载方式、序列化方式、DTO的选择,多一点参考。在本测试中,每个执行过程,对于Entity,我们将先用DataReader读出数据,使用索引将数据填充到Entity,序列化,再反序列化;对于Dataset,将先读取所有数据到Dataset,序列化,再反序列化,最后通过索引填充Dataset中的数据到Entity。也就是说,无论对Entity还是Dataset,我们都尽可能的取其最佳性能的执行方式,从而将性能瓶颈留在了序列化和反序列化方式上。您可以注意到,Dataset的序列化和反序列化性能是非常突出的,但是,我们基于Entity的自定义序列化方式的综合性能,超越了Dataset。

04/18补充:新增.NET JSON序列化对照。关于JSON的更多介绍请参见:http://www.json.org/。不过值得一提的是,官方提供的.Net实现写得那个烂得简直没话说。本测试使用Teddy修改由化后的.Net版本,性能是官方版本的30-40倍。

测试报告

就让我们先从一个测试报告开始,该测试读取Northwind数据库中Order Details Extended视图的所有数据,RepeatTime表示每个步骤(读数据、序列化、反序列化等等着每个步骤)被重复的次数。表中的时间数值单位为毫秒。

Ilungasoft Framework Data Access & Entities Serialization Performance Test


Repeat Time = 10
Title Read Data Time Serialize Time Serialized Xml Size DeserializeTime Total Run Time
Entities Xml Serialize 844 1766 702783 1750 4360
Entities Soap Serialize 328 4094 3031007 6203 10625
Entities Custom Serialize 359 594 598565 625 1578
DataSet Serialize 515 532 586200 2047 3094
Entities JSON Serialize 484 625 325298 641 1750

Repeat Time = 20
Title Read Data Time Serialize Time Serialized Xml Size Deserialize Time Total Run Time
Entities Xml Serialize 1093 2594 702783 3797 7484
Entities Soap Serialize 734 8032 3031007 12000 20766
Entities Custom Serialize 671 1188 598565 1234 3093
DataSet Serialize 657 1062 586200 6563 8282
Entities JSON Serialize 781 1094 325298 1093 2968

Repeat Time = 30
Title Read Data Time Serialize Time Serialized Xml Size Deserialize Time Total Run Time
Entities Xml Serialize 938 4140 702783 5547 10625
Entities Soap Serialize 1094 12578 3031007 18891 32563
Entities Custom Serialize 1140 1922 598565 2188 5250
DataSet Serialize 1156 1438 586200 14984 17578
Entities JSON Serialize 1141 1656 325298 1703 4500

Repeat Time = 40
Title Read Data Time Serialize Time Serialized Xml Size Deserialize Time Total Run Time
Entities Xml Serialize 1594 5047 702783 6968 13609
Entities Soap Serialize 1391 16844 3031007 26093 44328
Entities Custom Serialize 1875 2469 598565 2735 7079
DataSet Serialize 1828 2093 586200 25969 29890
Entities JSON Serialize 1719 2437 325298 2500 6656

解析

以上的测试中,除了Dataset Serialize是使用Dataset作为DTO来序列化和反序列化数据之外,其他的都是使用Entity方式来处理。Xml Serialize表示系统默认的XmlSerializer序列化方式,SoapSerilialize自然是系统默认的SoapFormatter序列化,CustomSerialize则是自定义的序列化方式(目前采用的是自定义序列化+XmlSerializer反序列化结合的方式)。Json Serialization为Teddy的Json优化版本。

测试代码

  1using System;
  2using System.Data;
  3using System.Collections;
  4using System.Configuration;
  5using System.Web;
  6using System.Web.Security;
  7using System.Web.UI;
  8using System.Web.UI.WebControls;
  9using System.Web.UI.WebControls.WebParts;
 10using System.Web.UI.HtmlControls;
 11using Ilungasoft.Framework.Common;
 12using Ilungasoft.Framework.Data.Facade;
 13
 14public partial class _Default : System.Web.UI.Page 
 15{
 16    protected void Page_Load(object sender, EventArgs e)
 17    {
 18        repeatTime = 10;
 19        reportView1.DataSource = DoTest();
 20
 21        repeatTime = 20;
 22        reportView2.DataSource = DoTest();
 23
 24        repeatTime = 30;
 25        reportView3.DataSource = DoTest();
 26
 27        repeatTime = 40;
 28        reportView4.DataSource = DoTest();
 29
 30        DataBind();
 31    }

 32
 33    private TestReport[] DoTest()
 34    {
 35        TestReport[] testReports = new TestReport[5];
 36        testReports[0= DoTest1();
 37        testReports[1= DoTest2();
 38        testReports[2= DoTest3();
 39        testReports[3= DoTest4();
 40        testReports[4= DoTest5();
 41
 42        return testReports;
 43    }

 44
 45    private long time;
 46    private DataSet ds;
 47    private Order_nbsp_Details_nbsp_Extended[] orderDetails;
 48    public string xml;
 49    private int repeatTime = 10;
 50
 51    private TestReport DoTest1()
 52    {
 53        TestReport testReport = new TestReport();
 54        testReport.Title = "Entities Xml Serialize";
 55
 56        time = System.Environment.TickCount;
 57        for (int i = 0; i < repeatTime; i++)
 58            orderDetails = DefaultGateway.SelectAll<Order_nbsp_Details_nbsp_Extended>();
 59        testReport.ReadDataTime = System.Environment.TickCount - time;
 60
 61        time = System.Environment.TickCount;
 62        for (int i = 0; i < repeatTime; i++)
 63            xml = SerializeHelper.SerializeArray(orderDetails);
 64        testReport.SerializeTime = System.Environment.TickCount - time;
 65        testReport.SerializedXmlSize = xml.Length;
 66
 67        time = System.Environment.TickCount;
 68        for (int i = 0; i < repeatTime; i++)
 69            orderDetails = SerializeHelper.Deserialize<Order_nbsp_Details_nbsp_Extended[]>(EntityFactory<Order_nbsp_Details_nbsp_Extended>.GetDynamicEntityType().MakeArrayType(), xml);
 70        testReport.DeserializeTime = System.Environment.TickCount - time;
 71
 72        testReport.TotalRunTime = testReport.ReadDataTime + testReport.SerializeTime + testReport.DeserializeTime;
 73
 74        return testReport;
 75    }

 76
 77    private TestReport DoTest2()
 78    {
 79        TestReport testReport = new TestReport();
 80        testReport.Title = "Entities Soap Serialize";
 81
 82        time = System.Environment.TickCount;
 83        for (int i = 0; i < repeatTime; i++)
 84            orderDetails = DefaultGateway.SelectAll<Order_nbsp_Details_nbsp_Extended>();
 85        testReport.ReadDataTime = System.Environment.TickCount - time;
 86
 87        time = System.Environment.TickCount;
 88        for (int i = 0; i < repeatTime; i++)
 89            xml = SerializeHelper.SoapSerialize(orderDetails);
 90        testReport.SerializeTime = System.Environment.TickCount - time;
 91        testReport.SerializedXmlSize = xml.Length;
 92
 93        time = System.Environment.TickCount;
 94        for (int i = 0; i < repeatTime; i++)
 95            orderDetails = SerializeHelper.SoapDeserialize<Order_nbsp_Details_nbsp_Extended[]>(xml);
 96        testReport.DeserializeTime = System.Environment.TickCount - time;
 97
 98        testReport.TotalRunTime = testReport.ReadDataTime + testReport.SerializeTime + testReport.DeserializeTime;
 99
100        return testReport;
101    }

102
103    private TestReport DoTest3()
104    {
105        TestReport testReport = new TestReport();
106        testReport.Title = "Entities Custom Serialize";
107
108        time = System.Environment.TickCount;
109        for (int i = 0; i < repeatTime; i++)
110            orderDetails = DefaultGateway.SelectAll<Order_nbsp_Details_nbsp_Extended>();
111        testReport.ReadDataTime = System.Environment.TickCount - time;
112
113        time = System.Environment.TickCount;
114        for (int i = 0; i < repeatTime; i++)
115            xml = SerializeHelper.SerializeEntityArray(orderDetails);
116        testReport.SerializeTime = System.Environment.TickCount - time;
117        testReport.SerializedXmlSize = xml.Length;
118
119        time = System.Environment.TickCount;
120        for (int i = 0; i < repeatTime; i++)
121            orderDetails = SerializeHelper.DeserializeEntityArray<Order_nbsp_Details_nbsp_Extended>(xml);
122        testReport.DeserializeTime = System.Environment.TickCount - time;
123
124        testReport.TotalRunTime = testReport.ReadDataTime + testReport.SerializeTime + testReport.DeserializeTime;
125
126        return testReport;
127    }

128
129    private TestReport DoTest4()
130    {
131        TestReport testReport = new TestReport();
132        testReport.Title = "DataSet Serialize";
133
134        time = System.Environment.TickCount;
135        for (int i = 0; i < repeatTime; i++)
136            ds = DefaultGateway.SelectDataSet("select * from [Order Details Extended]");
137        testReport.ReadDataTime = System.Environment.TickCount - time;
138
139        time = System.Environment.TickCount;
140        for (int i = 0; i < repeatTime; i++)
141        {
142            System.IO.MemoryStream ms = new System.IO.MemoryStream();
143            ds.WriteXml(ms);
144            xml = System.Text.UTF8Encoding.UTF8.GetString(ms.ToArray());
145        }
  
146        testReport.SerializeTime = System.Environment.TickCount - time;
147        testReport.SerializedXmlSize = xml.Length;
148
149        time = System.Environment.TickCount;
150        for (int i = 0; i < repeatTime; i++)
151        {
152            System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(xml));
153            ds.ReadXml(ms);
154            orderDetails = DefaultGateway.CreateList<Order_nbsp_Details_nbsp_Extended>(ds.Tables[0]);
155        }

156        testReport.DeserializeTime = System.Environment.TickCount - time;
157
158        testReport.TotalRunTime = testReport.ReadDataTime + testReport.SerializeTime + testReport.DeserializeTime;
159
160        return testReport;
161    }

162
163    private TestReport DoTest5()
164    {
165        TestReport testReport = new TestReport();
166        testReport.Title = "Entities JSON Serialize";
167
168        time = System.Environment.TickCount;
169        for (int i = 0; i < repeatTime; i++)
170            orderDetails = DefaultGateway.SelectAll<Order_nbsp_Details_nbsp_Extended>();
171        testReport.ReadDataTime = System.Environment.TickCount - time;
172
173        time = System.Environment.TickCount;
174        for (int i = 0; i < repeatTime; i++)
175            xml = SerializeHelper.JsonSerializeEntityArray(orderDetails);
176        testReport.SerializeTime = System.Environment.TickCount - time;
177        testReport.SerializedXmlSize = xml.Length;
178
179        time = System.Environment.TickCount;
180        for (int i = 0; i < repeatTime; i++)
181            orderDetails = SerializeHelper.JsonDeserializeEntityArray<Order_nbsp_Details_nbsp_Extended>(xml);
182        testReport.DeserializeTime = System.Environment.TickCount - time;
183
184        testReport.TotalRunTime = testReport.ReadDataTime + testReport.SerializeTime + testReport.DeserializeTime;
185
186        return testReport;
187    }

188}

更多代码我就不列举了,请自行下载下面的源码。

下载

下载测试框架及源码 (本示例源码包含在新版本的Ilungasoft Framework v1.4.4的目录中的dist\Sample5)

小结

本测试示例的目的并不是要终结Dataset作为DTO,尽管Entity作为DTO的性能是可以超越Dataset的,但Teddy觉得,Dataset的性能确实是相当好的,作为一个通用的DTO还是非常适合的(尤其是可以和异构的其他.Net系统方便共享和传递数据)。

补遗:
后续的测试中还发现,无论是XmlSerializer的序列化方式都不够稳定,特别是反序列化时,尤其是对符合类型和数组的序列化。因此,对于大多数基于.Net的程序构架,Teddy还是推荐使用Dataset作为DTO,除非对性能要求非常高时在使用自定义的序列化方式。

4/19:
更新了CustomSerizlize算法,使得其执行时间仅为Dataset的1/2 - 1/10。数据量越大,自定义序列化的优势就越明显。

新增的JSON序列化性能基本和自定义Xml 序列化相当,当数据量较大时,要比自定义Xml序列化方式稍好一点点。

但是,请注意,JSON的序列化后的文本大小只有Dataset的一半,对于需要远程通信的程序来讲,JSON方式将极大的减少需要通信的数据量,因此,JSON方式带来的实际的性能提升可能会更大。

原文地址:https://www.cnblogs.com/teddyma/p/376067.html