How to improve JSON deserialization speed in .Net? (JSON.net or other?)(如何提高 .Net 中的 JSON 反序列化速度?(JSON.net 还是其他?))
问题描述
我们正在考虑用 JSON(WCF 或其他)调用替换(部分或许多)经典"SOAP XML WCF 调用,因为它的开销较低且易于直接在 Javascript 中使用.目前,我们刚刚为我们的 Web 服务添加了一个额外的 Json 端点,并为一些操作添加了 WebInvoke 属性并对其进行了测试.一切正常,使用 C# .Net 客户端或 Javascript 客户端.到目前为止一切顺利.
We're considering replacing (some or many) 'classic' SOAP XML WCF calls by JSON (WCF or other) calls, because of the lower overhead and ease of use directly in Javascript. For now, we've just added an additional Json endpoint to our web service and added WebInvoke attributes to some operations and tested them. Everything works fine, using C# .Net clients or Javascript clients. So far so good.
然而,在 C# .Net 中将大的 JSON 字符串反序列化为对象似乎比反序列化 SOAP XML 慢得多.两者都使用 DataContract 和 DataMember 属性(完全相同的 DTO).我的问题是:这是预期的吗?我们可以做些什么来优化这种性能吗?或者我们是否应该仅将 JSON 用于我们注意到性能改进的较小请求.
However, it seems like deserializing big JSON strings to objects in C# .Net is much slower than deserializing SOAP XML. Both are using DataContract and DataMember attributes (exact same DTO). My question is: is this expected? Is there anything we can do to optimize this performance? Or should we consider JSON only for smaller requests where we DO notice performance improvements.
目前,我们为此测试选择了 JSON.net,即使它没有在这个测试用例中显示,它也应该比 .Net JSON 序列化更快.不知何故,ServiceStack 反序列化根本不起作用(没有错误,为 IList 返回 null).
For now we've chosen JSON.net for this test and even though it doesn't show in this test case, it's supposed to be faster than the .Net JSON serialization. Somehow the ServiceStack deserialization does not work at all (no error, returns null for the IList).
对于测试,我们会调用服务来收集房间列表.它返回一个 GetRoomListResponse,如果返回 5 个虚拟房间,JSON 如下所示:
For the test we do a service call to collect a list of rooms. It returns a GetRoomListResponse and in case of returning 5 dummy rooms, the JSON looks like this:
{"Acknowledge":1,"Code":0,"Message":null,"ValidateErrors":null,"Exception":null,"RoomList":[{"Description":"DummyRoom","Id":"205305e6-9f7b-4a6a-a1de-c5933a45cac0","Location":{"Code":"123","Description":"Location 123","Id":"4268dd65-100d-47c8-a7fe-ea8bf26a7282","Number":5}},{"Description":"DummyRoom","Id":"aad737f7-0caa-4574-9ca5-f39964d50f41","Location":{"Code":"123","Description":"Location 123","Id":"b0325ff4-c169-4b56-bc89-166d4c6d9eeb","Number":5}},{"Description":"DummyRoom","Id":"c8caef4b-e708-48b3-948f-7a5cdb6979ef","Location":{"Code":"123","Description":"Location 123","Id":"11b3f513-d17a-4a00-aebb-4d92ce3f9ae8","Number":5}},{"Description":"DummyRoom","Id":"71376c49-ec41-4b12-b5b9-afff7da882c8","Location":{"Code":"123","Description":"Location 123","Id":"1a188f13-3be6-4bde-96a0-ef5e0ae4e437","Number":5}},{"Description":"DummyRoom","Id":"b947a594-209e-4195-a2c8-86f20eb883c4","Location":{"Code":"123","Description":"Location 123","Id":"053e9969-d0ed-4623-8a84-d32499b5a8a8","Number":5}}]}
响应和 DTO 如下所示:
The Response and DTO's look like this:
[DataContract(Namespace = "bla")]
public class GetRoomListResponse
{
[DataMember]
public IList<Room> RoomList;
[DataMember]
public string Exception;
[DataMember]
public AcknowledgeType Acknowledge = AcknowledgeType.Success;
[DataMember]
public string Message;
[DataMember]
public int Code;
[DataMember]
public IList<string> ValidateErrors;
}
[DataContract(Name = "Location", Namespace = "bla")]
public class Location
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public int Number { get; set; }
[DataMember]
public string Code { get; set; }
[DataMember]
public string Description { get; set; }
}
[DataContract(Name = "Room", Namespace = "bla")]
public class Room
{
[DataMember]
public Guid Id { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public Location Location { get; set; }
}
那么我们的测试代码如下:
Then our test code is as follows:
static void Main(string[] args)
{
SoapLogin();
Console.WriteLine();
SoapGetRoomList();
SoapGetRoomList();
SoapGetRoomList();
SoapGetRoomList();
SoapGetRoomList();
SoapGetRoomList();
SoapGetRoomList();
Console.WriteLine();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
JsonDotNetGetRoomList();
Console.ReadLine();
}
private static void SoapGetRoomList()
{
var request = new TestServiceReference.GetRoomListRequest()
{
Token = Token,
};
Stopwatch sw = Stopwatch.StartNew();
using (var client = new TestServiceReference.WARPServiceClient())
{
TestServiceReference.GetRoomListResponse response = client.GetRoomList(request);
}
sw.Stop();
Console.WriteLine("SOAP GetRoomList: " + sw.ElapsedMilliseconds);
}
private static void JsonDotNetGetRoomList()
{
var request = new GetRoomListRequest()
{
Token = Token,
};
Stopwatch sw = Stopwatch.StartNew();
long deserializationMillis;
using (WebClient client = new WebClient())
{
client.Headers["Content-type"] = "application/json";
client.Encoding = Encoding.UTF8;
string requestData = JsonConvert.SerializeObject(request, JsonSerializerSettings);
var responseData = client.UploadString(GetRoomListAddress, requestData);
Stopwatch sw2 = Stopwatch.StartNew();
var response = JsonConvert.DeserializeObject<GetRoomListResponse>(responseData, JsonSerializerSettings);
sw2.Stop();
deserializationMillis = sw2.ElapsedMilliseconds;
}
sw.Stop();
Console.WriteLine("JSON.Net GetRoomList: " + sw.ElapsedMilliseconds + " (deserialization time: " + deserializationMillis + ")");
}
private static JsonSerializerSettings JsonSerializerSettings
{
get
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.CheckAdditionalContent = false;
serializerSettings.ConstructorHandling = ConstructorHandling.Default;
serializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
serializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
serializerSettings.NullValueHandling = NullValueHandling.Ignore;
serializerSettings.ObjectCreationHandling = ObjectCreationHandling.Replace;
serializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Error;
return serializerSettings;
}
}
现在我们运行这个应用程序返回 50、500 和 5000 个房间.对象不是很复杂.
Now we've run this application with returning 50, 500 and 5000 rooms. The objects are not very complex.
这些是结果;时间以毫秒为单位:
These are the results; times are in ms:
50 个房间:
SOAP GetRoomList: 37
SOAP GetRoomList: 5
SOAP GetRoomList: 4
SOAP GetRoomList: 4
SOAP GetRoomList: 9
SOAP GetRoomList: 5
SOAP GetRoomList: 5
JSON.Net GetRoomList: 289 (deserialization time: 91)
JSON.Net GetRoomList: 3 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
JSON.Net GetRoomList: 2 (deserialization time: 0)
500 间客房:
SOAP GetRoomList: 47
SOAP GetRoomList: 9
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8
SOAP GetRoomList: 8
JSON.Net GetRoomList: 301 (deserialization time: 100)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 12 (deserialization time: 8)
JSON.Net GetRoomList: 11 (deserialization time: 8)
JSON.Net GetRoomList: 11 (deserialization time: 8)
JSON.Net GetRoomList: 15 (deserialization time: 12)
5000 间客房:
SOAP GetRoomList: 93
SOAP GetRoomList: 51
SOAP GetRoomList: 58
SOAP GetRoomList: 60
SOAP GetRoomList: 53
SOAP GetRoomList: 53
SOAP GetRoomList: 51
JSON.Net GetRoomList: 405 (deserialization time: 175)
JSON.Net GetRoomList: 107 (deserialization time: 79)
JSON.Net GetRoomList: 108 (deserialization time: 82)
JSON.Net GetRoomList: 112 (deserialization time: 85)
JSON.Net GetRoomList: 105 (deserialization time: 79)
JSON.Net GetRoomList: 111 (deserialization time: 81)
JSON.Net GetRoomList: 110 (deserialization time: 82)
我在发布模式下运行应用程序.客户端和服务器都在同一台机器上.如您所见,与 WCF SOAP 使用的 XML 到对象的映射相比,使用 JSON 对许多(相同类型的)对象进行反序列化需要更多时间.该死,仅反序列化就比使用 SOAP 调用整个 Web 服务花费的时间更多.
I'm running the application in release mode. Both client and server on same machine. As you can see, deserialization of many (of the same type of) objects takes much more time with JSON than the XML to object mapping that WCF SOAP uses. Hell, deserialization alone takes more time than the entire web service call using SOAP.
对此有解释吗?XML(或 WCF SOAP 实现)是否在这方面提供了很大的优势,或者我可以在客户端更改任何东西(我宁愿不更改服务,但更改客户端 DTO 是可以接受的)尝试提高性能?感觉就像我已经在 JSON.net 端选择了一些设置,它们应该比默认设置更快,不是吗?这里的瓶颈似乎是什么?
Is there an explanation for this? Does XML (or the WCF SOAP implementation) offer a big advantage in this area or are there any things I can change on the client side (I'd rather not change the service, but changing the client side DTO's is acceptable) to try to improve performance? It feels like I already selected some settings on the JSON.net side that should make it faster than default settings, no? What seems to be the bottleneck here?
推荐答案
我花了一点时间阅读 JSON.NET 内部结构,我的结论是缓慢主要是由 反射引起的.
I have spent a little bit more time reading about JSON.NET internals, and my conclusion is that the slowness is caused mostly by reflection.
在 JSON.NET 网站上,我发现了一些不错的性能提示,我尝试了几乎所有的东西(JObject.Parse、自定义转换器等),但我无法挤出任何显着的性能改进.然后我阅读了整个网站上最重要的注释:
On the JSON.NET site i have found some nice performance tips, and i tried pretty much everything (JObject.Parse, Custom Converters etc.) but i couldn't squeeze out any significant performance improvement. Then i read the most important note on the whole site:
如果性能很重要并且您不介意使用更多代码来获得它,那么这是您的最佳选择.在此处阅读有关使用 JsonReader/JsonWriter 的更多信息
If performance is important and you don't mind more code to get it then this is your best choice. Read more about using JsonReader/JsonWriter here
所以我听取了建议,并实现了一个基本版本的 JsonReader 来有效地读取字符串:
So i listened to the advice and i implemented a basic version of a JsonReader to read the string efficiently:
var reader = new JsonTextReader(new StringReader(jsonString));
var response = new GetRoomListResponse();
var currentProperty = string.Empty;
while (reader.Read())
{
if (reader.Value != null)
{
if (reader.TokenType == JsonToken.PropertyName)
currentProperty = reader.Value.ToString();
if (reader.TokenType == JsonToken.Integer && currentProperty == "Acknowledge")
response.Acknowledge = (AcknowledgeType)Int32.Parse(reader.Value.ToString());
if (reader.TokenType == JsonToken.Integer && currentProperty == "Code")
response.Code = Int32.Parse(reader.Value.ToString());
if (reader.TokenType == JsonToken.String && currentProperty == "Message")
response.Message = reader.Value.ToString();
if (reader.TokenType == JsonToken.String && currentProperty == "Exception")
response.Exception = reader.Value.ToString();
// Process Rooms and other stuff
}
else
{
// Process tracking the current nested element
}
}
我认为练习很清楚,毫无疑问,这是您可以从 JSON.NET 中获得的最佳性能.
只是这个有限的代码比我有 500 个房间的盒子上的 Deserialize
版本快 12 倍,但当然映射还没有完成.但是,我很确定在最坏的情况下它至少会比反序列化快 5 倍.
Just this limited code is 12x faster than the Deserialize
version on my box with 500 rooms, but of course the mapping is not completed. However, i am pretty sure it will be at least 5x faster than deserialization in the worst-case.
查看此链接以获取有关 JsonReader 及其使用方法的更多信息:
Check out this link for more info about the JsonReader and how to use it:
http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm一个>
这篇关于如何提高 .Net 中的 JSON 反序列化速度?(JSON.net 还是其他?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何提高 .Net 中的 JSON 反序列化速度?(JSON.net 还是其他?)
- C# 通过连接字符串检索正确的 DbConnection 对象 2022-01-01
- Azure Active Directory 与 MVC,客户端和资源标识同一 2022-01-01
- 在 LINQ to SQL 中使用 contains() 2022-01-01
- 使用 rss + c# 2022-01-01
- CanBeNull和ReSharper-将其用于异步任务? 2022-01-01
- 是否可以在 .Net 3.5 中进行通用控件? 2022-01-01
- Windows 喜欢在 LINUX 中使用 MONO 进行服务开发? 2022-01-01
- 在 C# 中异步处理项目队列 2022-01-01
- 为什么 C# 中的堆栈大小正好是 1 MB? 2022-01-01
- 带问号的 nvarchar 列结果 2022-01-01