.Net에서 JSON 역직렬화 속도를 향상시키는 방법(JSON.net 또는 기타)
Javascript에서 직접 사용할 수 있는 오버헤드와 편의성이 낮기 때문에 "클래식" SOAP XML WCF 호출을 JSON(WCF 또는 기타) 호출로 대체하는 것을 고려하고 있습니다.현재 웹 서비스에 Json 엔드포인트를 추가하고 일부 작업에 WebInvoke 속성을 추가하여 테스트하고 있습니다.C# 를 사용하면, 모든 것이 정상적으로 동작합니다.넷 클라이언트 또는 Javascript 클라이언트.아직까지는 좋아.
다만, 큰 JSON 문자열을 C#의 오브젝트로 역직렬화하는 것 같습니다.Net은 SOAP XML을 역직렬화하는 것보다 훨씬 느립니다. 둘 다 DataContract 및 DataMember 특성(정확히 동일한 DTO)을 사용하고 있습니다.제 질문은 이게 예상된 일인가요?이 퍼포먼스를 최적화하기 위해 우리가 할 수 있는 일이 있나요?또는 퍼포먼스 향상을 알 수 있는 소규모 요구에 대해서만 JSON을 검토해야 합니다.
현재 이 테스트에서는 JSON.net을 선택했습니다.이 테스트 케이스에는 표시되지 않지만, 보다 빠를 것으로 예상됩니다.Net JSON 시리얼화ServiceStack의 시리얼화가 전혀 기능하지 않습니다(오류 없이 IList의 늘을 반환합니다).
테스트를 위해 서비스 콜을 실시하여 객실 목록을 수집합니다.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}}]}
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; }
}
그러면 우리의 테스트 코드는 다음과 같습니다.
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개의 객실이 반환되는 어플리케이션을 실행했습니다.그 물건들은 그다지 복잡하지 않다.
결과는 다음과 같습니다.시간은 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)
응용 프로그램을 릴리스 모드로 실행하고 있습니다.클라이언트와 서버가 모두 같은 머신상에 있습니다.보시다시피 JSON에서는 WCF SOAP에서 사용하는 XML에서 오브젝트로의 매핑보다 많은 (같은 유형의) 오브젝트의 역직렬화에 시간이 걸립니다.serialize 해제만 해도 SOAP을 사용한 웹 서비스 콜 전체보다 시간이 더 걸립니다.
이것에 대한 설명이 있나요?XML(또는 WCF SOAP 실장)은 이 분야에서 큰 이점을 제공합니까?아니면 클라이언트 측에서 변경할 수 있는 것이 있습니까(서비스는 변경하지 않지만 클라이언트 측 DTO는 변경할 수 있습니다).디폴트 설정보다 빠른 설정을 JSON.net 측에서 이미 선택한 것 같습니다.여기 병목현상이 뭐죠?
저는 JSON에 대해 읽으면서 조금 더 시간을 보냈습니다.NET 내부, 그리고 내 결론은 느림은 대부분 반영에 의해 야기된다는 것이다.
JSON에서.NET 사이트 퍼포먼스에 관한 힌트를 몇 가지 찾아서 거의 모든 것을 시도해 보았습니다(JOBject).Parse, Custom Converters 등)는 퍼포먼스의 대폭적인 향상을 억제할 수 없었습니다.그리고 사이트 전체에서 가장 중요한 메모를 읽습니다.
퍼포먼스가 중요하고, 그것을 취득하기 위해서 코드를 늘리는 것에 개의치 않는 경우는, 이것이 최선의 선택입니다.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
}
}
연습은 명확하고, 이것은 의심할 여지 없이 JSON에서 얻을 수 있는 최고의 퍼포먼스입니다.네트워크
.Deserialize
500개의 방이 있는 박스에 있는 버전이지만, 물론 지도는 완성되지 않았습니다.그러나 최악의 경우 역직렬화보다 최소 5배 이상 빠를 것으로 확신합니다.
JsonReader 및 사용 방법에 대한 자세한 내용은 다음 링크를 참조하십시오.
http://james.newtonking.com/json/help/html/ReadingWritingJSON.htm
저는 이제 ZenCoder와 mythz의 제안을 모두 사용했고 더 많은 테스트를 했습니다.첫 번째 테스트 셋업에서도 오류를 발견했습니다.릴리스 모드로 툴을 빌드하는 동안 Visual Studio에서 테스트 앱을 실행했기 때문에 디버깅 오버헤드가 추가되어 JSON에서는 큰 차이가 있었습니다.PC의 SOAP XML 측과 비교한 넷 측이기 때문에 초기 테스트 결과의 실제 차이는 이미 상당히 작았습니다.
어느 쪽이든, 서버(로컬 호스트)로부터 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
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
범례:
- JSON.Net (리플렉션) -> JsonConvert.Diserialize Object(같은 JSON).위와 같은 인터넷 코드)
- ServiceStack(리플렉션) -> JsonSerializer.디시리얼라이즈 From String
- SOAP Xml(수동 매핑) -> 위와 같은 SOAP 클라이언트콜과 DTO에서 모델로의 매핑 추가
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"), }), }), });
메모/개인 결론:
- 리플렉션 베이스의 디시리얼라이제이션도 실제 릴리스 모드(oops)에서의 SOAP XML 오브젝트 생성보다 그다지 느리지 않습니다.
- JSON에서의 수동 매핑.Net은 자동 매핑보다 속도가 빠르고 SOAP Xml 매핑 성능에 필적하며 특히 모델과 DTO가 다른 경우 매우 자유롭습니다.
- ServiceStack 수동 매핑은 실제로 전체 리플렉션 기반 매핑보다 느립니다.JSON보다 더 높은 수준의 수동 매핑이기 때문에 그런 것 같습니다.Net side, 일부 오브젝트 생성이 이미 거기서 발생하고 있는 것 같기 때문입니다.ServiceStack 측에서도 하위 레벨의 대안이 있을 수 있습니다.
- 이 모든 작업은 동일한 머신에서 서버/클라이언트 코드를 실행한 상태에서 수행되었습니다.별도의 클라이언트/서버 운영 환경에서는 네트워크를 통해 전송해야 하는 메시지가 훨씬 작기 때문에 JSON 솔루션이 SOAP XML을 능가할 것이라고 확신합니다.
- 이 상황에서는 JSON.Net Auto Mapping은 ServiceStack보다 약간 빠른 속도로 응답할 수 있습니다.
var receivedObject = JsonConvert.DeserializeObject<dynamic>(content);
훨씬 더 빠르게 동작합니다.
var receivedObject = JsonConvert.DeserializeObject<Product>(content);
게다가 이 속도는 한층 더 빨라집니다.
dynamic receivedObject = JObject.Parse(content); // The same goes for JArray.Parse()
여기에 IoT 애플리케이션 성능 향상에 도움이 되는 포인트를 2개 더 추가했습니다.저는 매일 수백만 건의 JSON 메시지를 받았습니다.
- Contract Resolver 인스턴스의 변경 사항
구코드
return JsonConvert.SerializeObject(this, Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
새 코드
모든 콜에 대해 계약 해결자 인스턴스를 생성하는 것이 아니라 단일 인스턴스를 사용하는 것
return JsonConvert.SerializeObject(this, Formatting.Indented,
new JsonSerializerSettings
{
ContractResolver = AppConfiguration.CamelCaseResolver
});
- JObject 작성 회피
구코드
JObject eventObj = JObject.Parse(jsonMessage);
eventObj.Add("AssetType", assetType); //modify object
JObject eventObj2 = JObject.Parse(jsonMessage);
eventObj.Add("id", id); //modify object
새로운 코드
JObject eventObj = JObject.Parse(jsonMessage);
eventObj.Add("AssetType", assetType); //modify object
JObject eventObj2 = (JObject)eventObj.DeepClone();
eventObj.Add("id", id); //modify object
성능상의 이점을 확인하기 위해 benchmarkdotnet을 사용하여 차이를 확인했습니다.이 링크도 체크해 주세요.
언급URL : https://stackoverflow.com/questions/26380184/how-to-improve-json-deserialization-speed-in-net-json-net-or-other
'programing' 카테고리의 다른 글
워드프레스의 클라우드 프런트 SSL 문제.리다이렉트가 너무 많다 (0) | 2023.04.03 |
---|---|
각도 필터는 작동하지만 "10$digest 반복 도달"의 원인이 됩니다. (0) | 2023.04.03 |
getDerivedStateFromError와 componentDidCatch의 차이점은 무엇입니까? (0) | 2023.03.29 |
리액트 라우터 v6의 컴포넌트 외부 탐색 (0) | 2023.03.29 |
추가 방법추가 방법두 줄 사이에 리액트 태그를 붙이는 게 어때요?두 줄 사이에 리액트 태그를 붙이는 게 어때요? (0) | 2023.03.29 |