Pro Yearly is on sale from $80 to $50! »

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

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

Af61fe2beec7f195d7418e0a474a4dfc?s=128

Kazuhiro Fujieda

July 29, 2020
Tweet

Transcript

  1. 静的クラスは遅いことが あるよ 藤枝 和宏 twitter: kfujieda fujieda@roundwide.com

  2. 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
  3. 速い 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
  4. 静的クラスを避ける 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) { // パーザーの本体 } }
  5. 静的クラスは遅い なぜか遅い 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
  6. ディスアセンブルしてみる .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
  7. 中身の大きなループは遅い • DynaJsonのJsonParserは大きなループ • 中身が全部L1キャッシュに乗らないと遅い 1 2 3 4 5

    6 7 8 プログラム L1キャッシュ 1 2 3 4 5 6 7 8 9 9
  8. なぜコードが大きいのか インライン展開の多用 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]; }
  9. なぜコードが大きいのか インライン展開の多用 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
  10. インライン展開をやめてみる • 差が縮まった • 静的クラスのキャッシュミスが減ったかな 0 2 4 6 8

    10 12 14 16 Static (inlined) Normal (inlined) Static Normal Time (ms) ←lower is better
  11. まとめ • 静的クラスはコードが大きくなることがある • 中身の大きすぎるループは遅くなることがある