Col*_*n B 43 .net c# performance json json.net
我们正在考虑通过JSON(WCF或其他)调用替换(部分或多个)'经典'SOAP XML WCF调用,因为它可以直接在Javascript中降低开销和易用性.目前,我们刚刚为我们的Web服务添加了一个额外的Json端点,并将WebInvoke属性添加到某些操作并对其进行了测试.一切正常,使用C#.Net客户端或Javascript客户端.到现在为止还挺好.
但是,似乎将大型JSON字符串反序列化为C#.Net中的对象要比反序列化SOAP XML慢得多.两者都使用DataContract和DataMember属性(完全相同的DTO).我的问题是:这是预期的吗?我们可以做些什么来优化这种性能?或者,我们是否应该仅针对较小的请求考虑JSON,我们会注意到性能的提高.
现在我们已经为这个测试选择了JSON.net,即使它没有在这个测试用例中显示,它应该比.Net JSON序列化更快.不知何故,ServiceStack反序列化根本不起作用(没有错误,IList返回null).
对于测试,我们进行服务电话以收集房间列表.它返回一个GetRoomListResponse,如果返回5个虚拟房间,JSON看起来像这样:
{"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}}]}
Run Code Online (Sandbox Code Playgroud)
Response和DTO看起来像这样:
[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; }
}
Run Code Online (Sandbox Code Playgroud)
然后我们的测试代码如下:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们运行此应用程序,返回50,500和5000个房间.对象不是很复杂.
这些是结果; 时间以毫秒为单位:
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)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
我正在以发布模式运行应用程序.客户端和服务器都在同一台机器上.正如您所看到的,使用JSON的许多(相同类型)对象的反序列化比WCF SOAP使用的XML到对象映射花费更多的时间.地狱,单独的反序列化比使用SOAP的整个Web服务调用花费更多的时间.
对此有解释吗?XML(或WCF SOAP实现)是否在这方面提供了很大的优势,或者我可以在客户端更改任何内容(我宁愿不更改服务,但更改客户端DTO是可接受的)尝试改善表现?感觉就像我已经在JSON.net一侧选择了一些设置,它应该比默认设置更快,不是吗?这似乎是什么瓶颈?
Far*_*ina 42
我花了一点时间阅读有关JSON.NET内部的内容,我的结论是缓慢主要是由反射造成的.
在JSON.NET网站上,我发现了一些不错的性能提示,我尝试了很多东西(JObject.Parse,自定义转换器等),但我无法挤出任何显着的性能提升.然后我读了整个网站上最重要的注释:
如果性能很重要并且您不介意获得更多代码,那么这是您的最佳选择.在这里阅读有关使用JsonReader/JsonWriter的更多信息
所以我听了建议,我实现了一个基本版本的JsonReader来有效地读取字符串:
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
}
}
Run Code Online (Sandbox Code Playgroud)
我认为这个练习很清楚,毫无疑问,这是你可以从JSON.NET中获得的最佳性能.
只有这个有限的代码比Deserialize我的500个房间的盒子上的版本快12倍,但当然映射没有完成.但是,我很确定在最坏的情况下,它比反序列化至少快5倍.
有关JsonReader以及如何使用它的更多信息,请查看此链接:
http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm
我现在已经使用了 ZenCoder 和 mythz 的建议,并且做了更多测试。我在第一个测试设置中也注意到了一个错误,因为当我在发布模式下构建该工具时,我仍然从 Visual Studio 启动测试应用程序,这仍然增加了一些调试开销,这在 JSON.Net 上产生了更大的差异与我的 PC 上的 SOAP XML 端相比,因此初始测试结果在实践中的差异已经小了很多。
不管怎样,下面是从服务器 (localhost) 收集 5000 / 50000 个房间的结果,包括将它们映射到模型。
5000间客房:
----- Test results for JSON.Net (reflection) -----
GetRoomList (5000): 107
GetRoomList (5000): 60
GetRoomList (5000): 65
GetRoomList (5000): 62
GetRoomList (5000): 63
----- Test results for ServiceStack (reflection) -----
GetRoomList (5000): 111
GetRoomList (5000): 62
GetRoomList (5000): 62
GetRoomList (5000): 60
GetRoomList (5000): 62
----- Test results for SOAP Xml (manual mapping) -----
GetRoomList (5000): 101
GetRoomList (5000): 47
GetRoomList (5000): 51
GetRoomList (5000): 49
GetRoomList (5000): 51
----- Test results for Json.Net (manual mapping) -----
GetRoomList (5000): 58
GetRoomList (5000): 47
GetRoomList (5000): 51
GetRoomList (5000): 49
GetRoomList (5000): 47
----- Test results for ServiceStack (manual mapping) -----
GetRoomList (5000): 91
GetRoomList (5000): 79
GetRoomList (5000): 64
GetRoomList (5000): 66
GetRoomList (5000): 77
Run Code Online (Sandbox Code Playgroud)
50000间客房:
----- Test results for JSON.Net (reflection) -----
GetRoomList (50000): 651
GetRoomList (50000): 628
GetRoomList (50000): 642
GetRoomList (50000): 625
GetRoomList (50000): 628
----- Test results for ServiceStack (reflection) -----
GetRoomList (50000): 754
GetRoomList (50000): 674
GetRoomList (50000): 658
GetRoomList (50000): 657
GetRoomList (50000): 654
----- Test results for SOAP Xml (manual mapping) -----
GetRoomList (50000): 567
GetRoomList (50000): 556
GetRoomList (50000): 561
GetRoomList (50000): 501
GetRoomList (50000): 543
----- Test results for Json.Net (manual mapping) -----
GetRoomList (50000): 575
GetRoomList (50000): 569
GetRoomList (50000): 515
GetRoomList (50000): 539
GetRoomList (50000): 526
----- Test results for ServiceStack (manual mapping) -----
GetRoomList (50000): 850
GetRoomList (50000): 796
GetRoomList (50000): 784
GetRoomList (50000): 805
GetRoomList (50000): 768
Run Code Online (Sandbox Code Playgroud)
传奇:
JSON.Net(手动映射) -> 使用基于上述 ZenCoder 代码的代码直接将 JSON 映射到模型,扩展为包括整个请求的映射(还有房间和位置)
ServiceStack(手动映射) ->请参阅以下代码(基于示例: https: //github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/UseCases/CentroidTests.cs)
var response = JsonObject.Parse(responseData).ConvertTo(x => new GetRoomListResponse()
{
Acknowledge = (AcknowledgeType)x.Get<int>("Acknowledge"),
Code = x.Get<int>("Code"),
Exception = x.Get("Exception"),
Message = x.Get("Message"),
RoomList = x.ArrayObjects("RoomList").ConvertAll<RoomModel>(y => new RoomModel()
{
Id = y.Get<Guid>("Id"),
Description = y.Get("Description"),
Location = y.Object("Location").ConvertTo<LocationModel>(z => new LocationModel()
{
Id = z.Get<Guid>("Id"),
Code = z.Get("Code"),
Description = z.Get("Description"),
Number = z.Get<int>("Number"),
}),
}),
});
Run Code Online (Sandbox Code Playgroud)笔记/个人结论:
| 归档时间: |
|
| 查看次数: |
39283 次 |
| 最近记录: |