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

静的クラスは遅いことがあるよ

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

 静的クラスは遅いことがあるよ

Avatar for Kazuhiro Fujieda

Kazuhiro Fujieda

July 29, 2020
Tweet

More Decks by Kazuhiro Fujieda

Other Decks in Technology

Transcript

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

    JsonObject.Parse(@"{ ""foo"": ""json"", ""nest"": {""foobar"": true} }"); string a1 = json.foo; // "json" bool a2 = json.nest.foobar; // true
  2. 速い citm_catalog.json (1.7MB)をパーズ .NET Core 3.1 Ubuntu 18.04 on Azure

    D2ds_v4 0 5 10 15 20 25 30 35 40 DynaJson Utf8Json Jil Newtonsoft.Json DynamicJson Time (ms) ←lower is better
  3. 静的クラスを避ける public class JsonParser { private static readonly JsonParser Instance

    = new JsonParser(); // インスタンスは一つ public static object Parse(TextReader reader, int maxDepth) { return Instance.ParseInternal(reader, maxDepth); // インスタンスメソッドを呼ぶ } private object ParseInternal(TextReader reader, int maxDepth) { // パーザーの本体 } }
  4. 静的クラスは遅い なぜか遅い 0 2 4 6 8 10 12 14

    Static Normal Time (ms) ←lower is better .NET Core 3.1 Ubuntu 18.04 on Azure D2ds_v4
  5. ディスアセンブルしてみる .NET Core 3.1.6 (CoreCLR 4.700.20.26901, CoreFX 4.700.20.31603), X64 RyuJIT

    JsonParser.ParseJsonInternal() ... mov r9d,[rbp+24] inc r9d mov [rbp+24],r9d inc dword ptr [rbp+28] cmp [rbp+20],r9d jne short M04_L01 ... Total bytes of code 7845 JsonParseStatic.Parse() ... mov r9d,[7FFE540EB864] inc r9d mov [7FFE540EB864],r9d inc dword ptr [7FFE540EB868] cmp [7FFE540EB860],r9d jne short M02_L01 ... Total bytes of code 9701
  6. なぜコードが大きいのか インライン展開の多用 case 'n': CheckToken("ull"); value.Type = JsonType.Null; break; private

    void CheckToken(string s) { Consume(); foreach (var ch in s) { if (ch != _nextChar) throw JsonParserException.ExpectingError($"'{ch}'", _position); Consume(); } } private void Consume() { _bufferIndex++; _position++; if (_available == _bufferIndex) { _bufferIndex = 0; _available = _reader.ReadBlock(_buffer, 0, _buffer.Length); if (_available == 0) { _isEnd = true; _nextChar = '¥0'; return; } } _nextChar = _buffer[_bufferIndex]; }
  7. なぜコードが大きいのか インライン展開の多用 case 'n': CheckToken("ull"); value.Type = JsonType.Null; break; cmp

    eax,6E ← 'n' jne near ptr M04_L104 mov r10d,[rbp+24] inc r10d mov [rbp+24],r10d inc dword ptr [rbp+28] cmp [rbp+20],r10d jne near ptr M04_L38 xor r9d,r9d mov [rbp+24],r9d mov rdx,[rbp+10] mov r9d,[rdx+8] mov rcx,[rbp+8] xor r8d,r8d mov rax,[rcx] mov rax,[rax+48] call qword ptr [rax+28] mov [rbp+20],eax cmp dword ptr [rbp+20],0 jne near ptr M04_L38 mov byte ptr [rbp+2E],1 mov word ptr [rbp+2C],0 M04_L08: mov r9,23E10009B48 mov rsi,[r9] xor edi,edi mov r12d,[rsi+8] test r12d,r12d jle short M04_L11 M04_L11: mov dword ptr [rsp+0B4],0FFF80001 jmp near ptr M04_L05 127 bytes
  8. インライン展開をやめてみる • 差が縮まった • 静的クラスのキャッシュミスが減ったかな 0 2 4 6 8

    10 12 14 16 Static (inlined) Normal (inlined) Static Normal Time (ms) ←lower is better