几个常用Json组件的性能测试
上一篇文章中我已经介绍了JsonBuilder方案的整体思路以及一个版本的雏形代码,他现在已经是可以使用的了,但是因为是实时反射的,所以效率并不高。
鉴于几位博友对Json转换组件的性能有兴趣,我先放几个常用组件的测试结果以供参考。下一篇在详细介绍JsonBuilder的优化工作
测试用例
using System; using System.Collections.Generic; /// <summary> 用户对象 /// </summary> public class User { public static User TestUser() {//这里我尽量构造一个看上去很复杂的对象,并且这个对象几乎涵盖了所有常用的类型 User user = new User(); user.UID = Guid.NewGuid(); user.Birthday = new DateTime(1986, 10, 29, 18, 00, 00); user.IsDeleted = false; user.Name = "blqw"; user.Sex = UserSex.Male; user.LoginHistory = new List<DateTime>(); user.LoginHistory.Add(DateTime.Today.Add(new TimeSpan(8, 00, 00))); user.LoginHistory.Add(DateTime.Today.Add(new TimeSpan(10, 10, 10))); user.LoginHistory.Add(DateTime.Today.Add(new TimeSpan(12, 33, 56))); user.LoginHistory.Add(DateTime.Today.Add(new TimeSpan(17, 25, 18))); user.LoginHistory.Add(DateTime.Today.Add(new TimeSpan(23, 06, 59))); user.Info = new UserInfo(); user.Info.Address = "广东省广州市"; user.Info.ZipCode = 510000; user.Info.Phone = new Dictionary<string, string>(); user.Info.Phone.Add("手机", "18688888888"); user.Info.Phone.Add("电话", "82580000"); user.Info.Phone.Add("短号", "10086"); user.Info.Phone.Add("QQ", "21979018"); user.Double = Double.NegativeInfinity; // user.Self = user; //这里是用来测试循环引用的解析情况 return user; } public User Self { get; set; } //User self /// <summary> 唯一ID /// </summary> public Guid UID { get; set; } /// <summary> 用户名称 /// </summary> public string Name { get; set; } /// <summary> 生日 /// </summary> public DateTime Birthday { get; set; } /// <summary> 性别 /// </summary> public UserSex Sex { get; set; } /// <summary> 是否删除标记 /// </summary> public bool IsDeleted { get; set; } /// <summary> 最近登录记录 /// </summary> public List<DateTime> LoginHistory { get; set; } /// <summary> 联系信息 /// </summary> public UserInfo Info { get; set; } public Double Double { get; set; } } /// <summary> 用户性别 /// </summary> public enum UserSex { /// <summary> 男 /// </summary> Male, /// <summary> 女 /// </summary> Female } /// <summary> 用户信息 /// </summary> public class UserInfo { /// <summary> 地址 /// </summary> public string Address { get; set; } /// <summary> 联系方式 /// </summary> public Dictionary<string, string> Phone { get; set; } /// <summary> 邮政编码 /// </summary> public int ZipCode { get; set; } }
using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Reflection.Emit; using System.Data; using fastJSON; using System.IO; using System.Threading; namespace blqw { class Program { static void Main(string[] args) { int count = 10000; var obj = User.TestUser(); TestJavaScriptSerializer(count, obj); TestNewtonsoftJson(count, obj); TestJayrockJson(count, obj); TestFastJson(count, obj); TestQuickJsonBuilder(count, obj); TestJsonBuilder(count, obj); } //测试QuickJsonBuilder性能 static void TestQuickJsonBuilder(int count, object obj) { Stopwatch sw = new Stopwatch(); Console.WriteLine(); Console.WriteLine("QuickJsonBuilder 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Restart(); for (int i = 0; i < count; i++) { QuickJsonBuilder jb = new QuickJsonBuilder(); jb.ToJson(obj); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } Console.WriteLine(); Console.WriteLine("结果:" + new QuickJsonBuilder().ToJson(obj)); Console.WriteLine("=============================================="); } //测试FastJson性能 static void TestFastJson(int count, object obj) { Stopwatch sw = new Stopwatch(); JSONParameters p = new JSONParameters(); p.EnableAnonymousTypes = p.IgnoreCaseOnDeserialize = p.ShowReadOnlyProperties = p.UseEscapedUnicode = p.UseExtensions = p.UseFastGuid = p.UseOptimizedDatasetSchema = p.UseUTCDateTime = p.UsingGlobalTypes = false; Console.WriteLine(); Console.WriteLine("fastJSON.NET 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Restart(); for (int i = 0; i < count; i++) { fastJSON.JSON.Instance.ToJSON(obj, p); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } Console.WriteLine(); Console.WriteLine("结果:" + fastJSON.JSON.Instance.ToJSON(obj, p)); Console.WriteLine("=============================================="); } //测试JayrockJson性能 static void TestJayrockJson(int count, object obj) { Stopwatch sw = new Stopwatch(); Console.WriteLine(); Console.WriteLine("Jayrock.Json 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Restart(); for (int i = 0; i < count; i++) { var writer = new Jayrock.Json.JsonTextWriter(); Jayrock.Json.Conversion.JsonConvert.Export(obj, writer); string str = writer.ToString(); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } var w = new Jayrock.Json.JsonTextWriter(); Jayrock.Json.Conversion.JsonConvert.Export(obj, w); Console.WriteLine(); Console.WriteLine("结果:" + w.ToString()); Console.WriteLine("=============================================="); } static void TestNewtonsoftJson(int count, object obj) { Stopwatch sw = new Stopwatch(); Console.WriteLine(); Console.WriteLine("Newtonsoft.Json 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Restart(); for (int i = 0; i < count; i++) { Newtonsoft.Json.JsonConvert.SerializeObject(obj); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } var w = Newtonsoft.Json.JsonConvert.SerializeObject(obj); Console.WriteLine(); Console.WriteLine("结果:" + w.ToString()); Console.WriteLine("=============================================="); } static void TestJavaScriptSerializer(int count, object obj) { Stopwatch sw = new Stopwatch(); Console.WriteLine(); Console.WriteLine("JavaScriptSerializer 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Restart(); for (int i = 0; i < count; i++) { new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(obj); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } var w = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(obj); Console.WriteLine(); Console.WriteLine("结果:" + w.ToString()); Console.WriteLine("=============================================="); } //测试JsonBuilder性能 static void JsonBuilder(int count, object obj) { Stopwatch sw = new Stopwatch(); Console.WriteLine(); Console.WriteLine("QuickJsonBuilder 每次" + count + " 共10次"); for (int j = 0; j < 10; j++) { sw.Reset(); sw.Start(); for (int i = 0; i < count; i++) { JsonBuilder jb = new JsonBuilder(); jb.ToJsonString(obj); } sw.Stop(); Console.Write(sw.ElapsedMilliseconds + "ms | "); } Console.WriteLine(); Console.WriteLine("结果:" + new JsonBuilder().ToJsonString(obj)); Console.WriteLine("=============================================="); } } }
测试方案1:微软自带的的 JavaScriptSerializer
引用组件:System.Web.Extensions(微软自带了,引用下就好)
string str = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(User.TestUser()); //{"Self":null,"UID":"6f50b429-5c13-4875-a29a-e4bd8d7b2772","Name":"blqw","Birthday":"\/Date(530964000000)\/","Sex":0,"IsDeleted":false,"LoginHistory":["\/Date(1377129600000)\/","\/Date(1377137410000)\/","\/Date(1377146036000)\/","\/Date(1377163518000)\/","\/Date(1377184019000)\/"],"Info":{"Address":"广东省广州市","Phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"ZipCode":510000},"Double":-Infinity}
测试方案2:Newtonsoft.Json
引用组件:Newtonsoft.Json.Net35.dll
string str = Newtonsoft.Json.JsonConvert.SerializeObject(User.TestUser()); //{"Self":null,"UID":"6f50b429-5c13-4875-a29a-e4bd8d7b2772","Name":"blqw","Birthday":"\/Date(530964000000+0800)\/","Sex":0,"IsDeleted":false,"LoginHistory":["\/Date(1377129600000+0800)\/","\/Date(1377137410000+0800)\/","\/Date(1377146036000+0800)\/","\/Date(1377163518000+0800)\/","\/Date(1377184019000+0800)\/"],"Info":{"Address":"广东省广州市","Phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"ZipCode":510000},"Double":-Infinity}
测试方案3:Jayrock.Json
引用组件:Jayrock.Json.dll
JsonTextWriter jtw = new Jayrock.Json.JsonTextWriter(); Jayrock.Json.Conversion.JsonConvert.Export(User.TestUser(), jtw); string str = jtw.ToString(); //{"uID":"6f50b429-5c13-4875-a29a-e4bd8d7b2772","name":"blqw","birthday":"1986-10-29T18:00:00.0000000+08:00","sex":"Male","isDeleted":false,"loginHistory":["2013-08-22T08:00:00.0000000+08:00","2013-08-22T10:10:10.0000000+08:00","2013-08-22T12:33:56.0000000+08:00","2013-08-22T17:25:18.0000000+08:00","2013-08-22T23:06:59.0000000+08:00"],"info":{"address":"广东省广州市","phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"zipCode":510000},"double":-Infinity}
测试方案4:fastJSON.NET
引用组件:fastJSON.dll
JSONParameters p = new JSONParameters(); p.EnableAnonymousTypes = p.IgnoreCaseOnDeserialize = p.ShowReadOnlyProperties = p.UseEscapedUnicode = p.UseExtensions = p.UseFastGuid = p.UseOptimizedDatasetSchema = p.UseUTCDateTime = p.UsingGlobalTypes = false;//所有参数为false,执行速度最快 string str = fastJSON.JSON.Instance.ToJSON(User.TestUser(), p); //{"Self":null,"UID":"6f50b429-5c13-4875-a29a-e4bd8d7b2772","Name":"blqw","Birthday":"1986-10-29 18:00:00","Sex":"Male","IsDeleted":false,"LoginHistory":["2013-08-22 08:00:00","2013-08-22 10:10:10","2013-08-22 12:33:56","2013-08-22 17:25:18","2013-08-22 23:06:59"],"Info":{"Address":"广东省广州市","Phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"ZipCode":510000},"Double":-Infinity}
测试方案5,6:QuickJsonBuilder,JsonBuilder
ps:以上几种类型都不支持循环引用,如果有,直接抛出异常
说明:QuickJsonBuilder就是我上篇中介绍的JsonBuilder的终极形态
QuickJsonBuilder jb = new QuickJsonBuilder(); jb.ToJson(User.TestUser());
//{"Self":null,"UID":"6f50b429-5c13-4875-a29a-e4bd8d7b2772","Name":"blqw","Birthday":"1986-10-29 18:00:00","Sex":"Male","IsDeleted":false,"LoginHistory":["2013-08-22 08:00:00","2013-08-22 10:10:10","2013-08-22 12:33:56","2013-08-22 17:25:18","2013-08-22 23:06:59"],"Info":{"Address":"广东省广州市","Phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"ZipCode":510000},"Double":-Infinity}
ps:当Self指向自己本身的时候,将返回 "Self":undefined 用以区分空对象
测试方案7:ServiceStack.Text
引用组件:ServiceStack.Text.dll 抱歉我只找到4.0的
这个是后来补上的,测试代码中没有这个...
User.TestUser().ToJson(); //{"UID":"48d4624ad0c8475094d00fc769c7129f","Name":"blqw","Birthday":"\/Date(530964000000+0800)\/","Sex":"Male","IsDeleted":false,"LoginHistory":["\/Date(1377129600000+0800)\/","\/Date(1377137410000+0800)\/","\/Date(1377146036000+0800)\/","\/Date(1377163518000+0800)\/","\/Date(1377184019000+0800)\/"],"Info":{"Address":"广东省广州市","Phone":{"手机":"18688888888","电话":"82580000","短号":"10086","QQ":"21979018"},"ZipCode":510000},"Double":-Infinity}
ps:依然不支持循环引用,抛出堆栈异常
测试结果(单位 "毫秒")
循环次数 | 测试组件 | 第一轮 | 第二轮 | 第三轮 | 第四轮 | 第五轮 |
1次 | JavaScriptSerializer | 12 |
0 | 0 | 0 | 0 |
Newtonsoft.Json | 208 | 0 | 0 | 0 | 0 | |
Jayrock.Json | 85 | 0 | 0 | 0 | 0 | |
fastJSON.NET | 47 | 0 | 0 | 0 | 0 | |
QuickJsonBuilder | 37 | 0 | 0 | 0 | 0 | |
JsonBuilder | 22 | 0 | 0 | 0 | 0 | |
ServiceStack.Text | 138 | 0 | 0 | 0 | 0 | |
100次 | JavaScriptSerializer | 23 | 7 | 7 | 8 | 7 |
Newtonsoft.Json | 201 | 2 | 3 | 2 | 2 | |
Jayrock.Json | 77 | 8 | 9 | 9 | 8 | |
fastJSON.NET | 41 | 1 | 1 | 1 | 1 | |
QuickJsonBuilder | 36 | 1 | 1 | 1 | 1 | |
JsonBuilder | 26 | 3 | 4 | 3 | 3 | |
ServiceStack.Text | 139 | 2 | 2 | 2 | 2 | |
10000次 | JavaScriptSerializer | 765 | 751 | 752 | 751 | 749 |
Newtonsoft.Json | 437 | 253 | 251 | 248 | 243 | |
Jayrock.Json | 967 | 905 | 965 | 913 | 952 | |
fastJSON.NET | 239 | 181 | 200 | 167 | 166 | |
QuickJsonBuilder | 171 | 128 | 132 | 136 | 129 | |
JsonBuilder | 418 | 386 | 388 | 391 | 360 | |
ServiceStack.Text | 367 | 216 | 224 | 238 | 223 |
测试结果仅供参考
总结
可以看出基本所有组件在第一轮用时都是最长的,应该是在处理一些初始化和缓存的工作。
所以呢,我特别给出了1次的循环结果,
JavaScriptSerializer 可以看出在100次以内的时候微软的还是有一些优势的,随着次数增加性能就渐渐跟不上了
Newtonsoft.Json一直表现平平啊,没有特别突出就地方,就跟JsonBuilder比性能也没有好到那里去
Jayrock.Json 是昨天一位博友告诉我的,据说是淘宝sdk在用的,性能只能说,好差。。。
fastJSON.NET 确实是所有组件中最快的,QuickJsonBuilder也从中借鉴了一些处理方式
ServiceStack.Text 中规中矩吧比Newtonsoft.Json好一点点.就是我只找到4.0的拓展方法,不知道2.0下有没有相应的组件
好吧 我自己的组件我就不做评价了
我发布的代码,没有任何版权,遵守WTFPL协议(如有引用,请遵守被引用代码的协议)
qq群:5946699 希望各位喜爱C#的朋友可以在这里交流学习,分享编程的心得和快乐