前言

现有的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 ? ikende.com 2019
email:henryfan@msn.com
github:https://github.com/ikende
——————————————————————————-
|name                          | round| threads|     count| use time(s)|   sec|
——————————————————————————-
|bytes_json                    |     1|       1|   1000000|        5.57|179580|
——————————————————————————-
|streampool_json               |     1|       1|   1000000|        5.44|183898|
——————————————————————————-
|bytes_json_list               |     1|       1|   1000000|       43.01| 23248|
——————————————————————————-
|streampool_json_list          |     1|       1|   1000000|       42.75| 23391|
——————————————————————————-
|bytes_json                    |     1|       2|   1000000|        2.81|355990|
——————————————————————————-
|streampool_json               |     1|       2|   1000000|        2.95|338969|
——————————————————————————-
|bytes_json_list               |     1|       2|   1000000|       23.16| 43180|
——————————————————————————-
|streampool_json_list          |     1|       2|   1000000|        22.4| 44650|
——————————————————————————-
|bytes_json                    |     1|       4|   1000000|        1.51|661246|
——————————————————————————-
|streampool_json               |     1|       4|   1000000|        1.57|636130|
——————————————————————————-
|bytes_json_list               |     1|       4|   1000000|       13.35| 74915|
——————————————————————————-
|streampool_json_list          |     1|       4|   1000000|       11.97| 83508|
——————————————————————————-
|bytes_json                    |     1|       8|   1000000|         .83|1199453|
——————————————————————————–
|streampool_json               |     1|       8|   1000000|         .88|1142495|
——————————————————————————–
|bytes_json_list               |     1|       8|   1000000|        9.24|108228|
——————————————————————————-
|streampool_json_list          |     1|       8|   1000000|        6.75|148132|
——————————————————————————-
|bytes_json                    |     1|      16|   1000000|         .56|1795910|
——————————————————————————–
|streampool_json               |     1|      16|   1000000|         .74|1344851|
——————————————————————————–
|bytes_json_list               |     1|      16|   1000000|        7.67|130424|
——————————————————————————-
|streampool_json_list          |     1|      16|   1000000|        4.61|216860|
——————————————————————————-
|bytes_json                    |     1|      24|   1000000|         .54|1849769|
——————————————————————————–
|streampool_json               |     1|      24|   1000000|         .73|1361382|
——————————————————————————–
|bytes_json_list               |     1|      24|   1000000|        7.61|131373|
——————————————————————————-
|streampool_json_list          |     1|      24|   1000000|         4.7|212779|
——————————————————————————-
|bytes_json                    |     1|      32|   1000000|         .55|1825484|
——————————————————————————–
|streampool_json               |     1|      32|   1000000|         .75|1339050|
——————————————————————————–
|bytes_json_list               |     1|      32|   1000000|        8.01|124885|
——————————————————————————-
|streampool_json_list          |     1|      32|   1000000|        5.21|192038|
——————————————————————————-
test completed!

总结

从测试结果来看,如果序列化的对象比小,那可以直接基于bytes的方式。虽然会产生新的bytes对象,不过由于对象比较小,引起的分配和回收并没有对象池操作上的损耗高。不过如果对象相对复杂些的情况下,那对象池的作用就能发挥出来,并发越大其作用越明显!,当并发线程数达到8的时候,效率已经明显抛开!由于业务上的数据信息都相对比较复杂些,所以在处理上还是建议通过对象池的方式来完成json序列化处理。

下载测试代码:http://xiazai.jb51.net/201904/yuanma/jsonsample(jb51).rar

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对www.887551.com的支持。