Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NaN BoxingによるJSONパーサーの高速化

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

NaN BoxingによるJSONパーサーの高速化

DynaJsonの高速化で使っているNaN Boxingという技術を紹介する。

Avatar for Kazuhiro Fujieda

Kazuhiro Fujieda

February 14, 2020
Tweet

More Decks by Kazuhiro Fujieda

Other Decks in Programming

Transcript

  1. DynaJson • https://github.com/fujieda/DynaJson/ • 高速なJSONパーサー • DynamicJson互換 var json =

    JsonObject.Parse(@"{ ""foo"": ""json"", ""nest"": {""foobar"": true} }"); var a1 = json.foo; // "json" var a2 = json.nest.foobar; // true
  2. 速い citm_catalog.json (1.7MB)⇒DynamicObject 11.849 21.123 34.711 50.772 58.141 0 10

    20 30 40 50 60 70 DynaJson Utf8Json Jil Newtonsoft.Json DynamicJson Time (ms) ←lower is better
  3. 16バイトに格納する • Explicitレイアウトでフィールドを重ねる • doubleの上4バイトにJsonTypeを重ねる ⇒ NaN Boxing [StructLayout(LayoutKind.Explicit)] internal

    struct InternalObject { [FieldOffset(4)] public JsonType Type; [FieldOffset(0)] public double Number; [FieldOffset(8)] public string String; [FieldOffset(8)] public JsonArray Array; [FieldOffset(8)] public JsonDictionary Dictionary; }
  4. NaNに値を詰める enum JsonType : uint { Null = 0xfff80001, True

    = 0xfff80002, False = 0xfff80003, String = 0xfff80004, Array = 0xfff80005, Object = 0xfff80006 } static object ToValue(InternalObject obj) { switch (obj.Type) { case JsonType.Null: return null; case JsonType.True: return true; case JsonType.False: return false; case JsonType.String: return obj.String; case JsonType.Array: case JsonType.Object: return new JsonObject(obj); default: return obj.Number; } }
  5. NaN Boxingの効果 11.849 13.418 16.294 0 5 10 15 20

    struct 16 struct 24 class Time (ms) ←lower is better