前言
现有的webapi一般都基于JSON的格式来处理数据,由于JSON是一个文本类的序列化协议所以在性能上自然就相对低效一些。在.net中常用Newtonsoft.Json是最常用的组件,由于提供简便基于完整的json的String方法使用起来非常方便;但也正是这原因导致Newtonsoft.Json在性能上一直被说慢,虽然Newtonsoft.Json提供Stream的方式来处理JSON不过想复用writer和reader还是需要一些应用技巧。如果需要在网络通讯中应用JSON,那在这里介绍一下SpanJson这个组件,并通过一些测试来讲述如何使用它。
SpanJson介绍
SpanJson是一个性能相对不错的JSON组件,组件直接提供了byte[]和stream两种操作方式,而这两种方式非常适合在构建自有网络通讯上使用。通过这些基础的字节和流结构来处理可以相对降低一个大string的开销。不过这个组件的热度并不高,完善成度暂还不如Newtonsoft.Json,不过asp.net core 在FrameworkBenchmarks测试上已经引入。可以尝试一下使用,组件开源地址: https://github.com/Tornhoof/SpanJson (本地下载)
性能测试
组件提供的方法相对比较少,从设计上来说更多是针对通讯方面的支持。基于Stream的序列化可以直接挂载在NetStream上,这样可以节省数据复制带来的开销。不过反序列化不能直接在有混合数据的Stream上进行,这或多或少有些可惜。从issues的解答来看作者也不太愿意在混合数据流上进行调整。接下来针对bytes和Stream使用进行一个性能测试,而Stream则采用一个可复用池的设计
MemoryStream 池的设计
public class MemoryStreamPool
{
private static System.Collections.Concurrent.ConcurrentStack<JsonMemoryStream> mPool = new System.Collections.Concurrent.ConcurrentStack<JsonMemoryStream>();
public static Stream Pop()
{
if (!mPool.TryPop(out JsonMemoryStream result))
{
result = new JsonMemoryStream(1024 * 32);
}
return result;
}
public class JsonMemoryStream : MemoryStream
{
public JsonMemoryStream(int size) : base(size) { }
protected override void Dispose(bool disposing)
{
MemoryStreamPool.Push(this);
}
}
private static void Push(JsonMemoryStream stream)
{
stream.Position = 0;
stream.SetLength(0);
mPool.Push(stream);
}
}
测试内容
测试的方式主要针对一个简单的对象和一个对象列表,然后在不同线程下bytes和Stream pool这两种方式的性能差别;压测的线程数据分别是1,2,4,8,16,24,32,每次测试执行的总数是100万次,然后统计出执行需要的时间和并发量。 测试代码:
public class Bytes_JSON : BeetleX.Benchmark.BenchmarkBase
{
protected override void OnTest()
{
while (Increment())
{
var data = SpanJson.JsonSerializer.NonGeneric.Utf8.Serialize(DataHelper.Defalut.Employees[0]);
var employees = SpanJson.JsonSerializer.Generic.Utf8.Deserialize<Employee>(data);
}
}
}
public class StreamPool_JSON : BeetleX.Benchmark.BenchmarkBase
{
protected override void OnTest()
{
RunTest();
}
private async void RunTest()
{
while (Increment())
{
using (Stream stream = MemoryStreamPool.Pop())
{
await SpanJson.JsonSerializer.NonGeneric.Utf8.SerializeAsync(DataHelper.Defalut.Employees[0], stream);
stream.Position = 0;
var employees = await SpanJson.JsonSerializer.Generic.Utf8.DeserializeAsync<Employee>(stream);
}
}
}
}
public class Bytes_JSON_List : BeetleX.Benchmark.BenchmarkBase
{
protected override void OnTest()
{
while (Increment())
{
var data = SpanJson.JsonSerializer.NonGeneric.Utf8.Serialize(DataHelper.Defalut.Employees);
var employees = SpanJson.JsonSerializer.Generic.Utf8.Deserialize<List<Employee(data);
}
}
}
public class StreamPool_JSON_List : BeetleX.Benchmark.BenchmarkBase
{
protected override void OnTest()
{
RunTest();
}
private async void RunTest()
{
while (Increment())
{
using (Stream stream = MemoryStreamPool.Pop())
{
await SpanJson.JsonSerializer.NonGeneric.Utf8.SerializeAsync(DataHelper.Defalut.Employees, stream);
stream.Position = 0;
var employees = await SpanJson.JsonSerializer.Generic.Utf8.DeserializeAsync<List<Employee(stream);
}
}
}
}
测试结果
C:\Users\Administrator\Desktop\json_test>dotnet JsonSample.dll
BeetleX.Benchmark [0.5.4.0] Copyright "color: #ff0000">总结
从测试结果来看,如果序列化的对象比小,那可以直接基于bytes的方式。虽然会产生新的bytes对象,不过由于对象比较小,引起的分配和回收并没有对象池操作上的损耗高。不过如果对象相对复杂些的情况下,那对象池的作用就能发挥出来,并发越大其作用越明显!,当并发线程数达到8的时候,效率已经明显抛开!由于业务上的数据信息都相对比较复杂些,所以在处理上还是建议通过对象池的方式来完成json序列化处理。
下载测试代码:http://xiazai.jb51.net/201904/yuanma/JsonSample(jb51).rar
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。