$30 off During Our Annual Pro Sale. View Details »

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

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

Kazuhiro Fujieda

July 29, 2020
Tweet

More Decks by Kazuhiro Fujieda

Other Decks in Technology

Transcript

  1. 静的クラスは遅いことが
    あるよ
    藤枝 和宏
    twitter: kfujieda
    [email protected]

    View Slide

  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

    View Slide

  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

    View Slide

  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)
    {
    // パーザーの本体
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  7. 中身の大きなループは遅い
    • DynaJsonのJsonParserは大きなループ
    • 中身が全部L1キャッシュに乗らないと遅い
    1
    2
    3
    4
    5
    6
    7
    8
    プログラム
    L1キャッシュ
    1
    2
    3
    4
    5
    6
    7
    8
    9
    9

    View Slide

  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];
    }

    View Slide

  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

    View Slide

  10. インライン展開をやめてみる
    • 差が縮まった
    • 静的クラスのキャッシュミスが減ったかな
    0 2 4 6 8 10 12 14 16
    Static
    (inlined)
    Normal
    (inlined)
    Static
    Normal
    Time (ms) ←lower is better

    View Slide

  11. まとめ
    • 静的クラスはコードが大きくなることがある
    • 中身の大きすぎるループは遅くなることがある

    View Slide