我对如何使用Newtonsoft.Json反序列化HTTP响应JSON有效负载的两种方法的性能(速度,内存使用)进行比较感兴趣。
我知道Newtonsoft.Json's Performance Tips使用流,但是我想知道更多并且有硬数字。我已经使用BenchmarkDotNet编写了简单的基准测试,但是我对结果感到困惑(请参见下面的数字)。
我得到了什么
我还没有时间做适当的分析(还),我对使用流方法的内存开销感到惊讶(如果没有错误)。整个代码为here。
"stylePreprocessorOptions": {
"includePaths": [
"./src/styles"
]
}
的使用;模拟MemoryStream
及其内容; ...)我正在准备HttpResponseMessage
以在基准测试运行中反复使用:
MemoryStream
[GlobalSetup]
public void GlobalSetup()
{
var resourceName = _resourceMapping[typeof(T)];
using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
_memory = new MemoryStream();
resourceStream.CopyTo(_memory);
}
_iterationRepeats = _repeatMapping[typeof(T)];
}
我们首先从流读取JSON到字符串,然后运行反序列化-正在分配另一个字符串,然后将其用于反序列化。
[Benchmark(Description = "Stream d13n")]
public async Task DeserializeStream()
{
for (var i = 0; i < _iterationRepeats; i++)
{
var response = BuildResponse(_memory);
using (var streamReader = BuildNonClosingStreamReader(await response.Content.ReadAsStreamAsync()))
using (var jsonReader = new JsonTextReader(streamReader))
{
_serializer.Deserialize<T>(jsonReader);
}
}
}
[Benchmark(Description = "String d13n")]
public async Task DeserializeString()
{
for (var i = 0; i < _iterationRepeats; i++)
{
var response = BuildResponse(_memory);
var content = await response.Content.ReadAsStringAsync();
JsonConvert.DeserializeObject<T>(content);
}
}
重复10000次
重复1000次
重复了100次
我深入研究private static HttpResponseMessage BuildResponse(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
var content = new StreamContent(stream);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = content
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static StreamReader BuildNonClosingStreamReader(Stream inputStream) =>
new StreamReader(
stream: inputStream,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1024,
leaveOpen: true);
的来源,发现从JsonConvert
:JsonConvert:816反序列化时,它内部使用JsonTextReader
和StringReader
。流也在那里(当然!)。
然后,我决定进一步研究string
本身,一见钟情-总是分配数组缓冲区(StreamReader
):StreamReader:244,这解释了它的内存使用情况。
这给了我“为什么”的答案。解决方案很简单-实例化byte[]
时使用较小的缓冲区大小-最小缓冲区大小默认为128(请参见StreamReader
),但是您可以提供任何值StreamReader.MinBufferSize
(检查ctor重载之一)。
当然,缓冲区大小对数据处理有影响。然后回答应该使用什么缓冲区大小:这取决于。当期望较小的JSON响应时,我认为保留较小的缓冲区是安全的。
答案 0 :(得分:2)
经过一番摆弄之后,我发现使用StreamReader
时内存分配背后的原因。原始帖子已更新,但请在此处重述:
StreamReader
使用默认bufferSize
设置为1024。StreamReader
的每个实例都分配该大小的字节数组。这就是为什么我在基准测试中看到这些数字的原因。
当我将bufferSize
设置为最低值128
时,结果似乎要好得多。