Writing a Fast JSON Parser
Hi all, author here.<p>Several people have raised questions about SIMD.  For many workloads, SIMD is obviously a huge win.  But in my experience, for problems like this, the gains are marginal and the additional complexity often isn&#x27;t worth it.  e.g. Do you have a build-time option? Check at runtime at every call? Use templates to check at startup and run a different code path?<p>That said, it never hurts to try, so I implemented the fast string path in AVX2 (see <a href="https:&#x2F;&#x2F;github.com&#x2F;chadaustin&#x2F;sajson&#x2F;commit&#x2F;e87da27883ffe0a3c50cd368d13e645659990fac" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;chadaustin&#x2F;sajson&#x2F;commit&#x2F;e87da27883ffe0a3...</a>).  It actually benchmarks slower than the scalar code: <a href="https:&#x2F;&#x2F;gist.github.com&#x2F;chadaustin&#x2F;6fe8d250ac8402ad70a724e7aa61484b" rel="nofollow">https:&#x2F;&#x2F;gist.github.com&#x2F;chadaustin&#x2F;6fe8d250ac8402ad70a724e7a...</a><p>I couldn&#x27;t tell you why - it&#x27;s possible something could be tweaked to make it actually execute faster, or that enabling -mavx2 causes other code to get slower, but I wanted to illustrate that enabling SIMD is not always an automatic go fast button.<p>I did look at the instructions generated and they seem reasonable:<p><pre><code>    .LBB5_118:
        vmovdqu	ymm5, ymmword ptr [rbx - 32]
        vpcmpgtb	ymm6, ymm5, ymm0
        vpcmpeqb	ymm7, ymm5, ymm1
        vpcmpeqb	ymm8, ymm5, ymm2
        vpcmpeqb	ymm5, ymm5, ymm3
        vpxor	ymm5, ymm5, ymm4
        vpor	ymm6, ymm8, ymm6
        vpor	ymm5, ymm5, ymm6
        vpor	ymm5, ymm5, ymm7
        vpmovmskb	esi, ymm5
        test	esi, esi
        jne	.LBB5_148
        mov	rbp, rax
        sub	rbp, rbx
        mov	rsi, rbx
        lea	rbx, [rbx + 32]
        cmp	rbp, 31
        jg	.LBB5_118
        jmp	.LBB5_120

    .LBB5_148:
        add	rbx, -32
        bsr	eax, esi
        xor	eax, 31
        lea	rsi, [rbx + rax]
        mov	bl, byte ptr [rbx + rax]
    .LBB5_125:
        mov	r10, qword ptr [rsp + 24] # 8-byte Reload
        mov	r9b, byte ptr [rsp + 39] # 1-byte Reload
        sub	rcx, r8
        movzx	eax, bl
        cmp	eax, 34
        jne	.LBB5_127
</code></pre>
I suppose it&#x27;s also worth pointing out that, while string scanning is an important part of JSON parsing, the instruction histogram is generally spread across the entire parser.  That is, a doubling in string parsing performance would only help overall parse times by 10-20% maybe.<p>Edit: Upon thinking about it, it&#x27;s probably slower because there&#x27;s a dependency chain through a pile of high-latency vector instructions all the way to the bsr (should be bsf, I fixed on the branch) to the next loop iteration&#x27;s load.  The branch predictor doesn&#x27;t have much opportunity to help us out here.  You could look at Agner Fog&#x27;s tables and calculate the critical path latency of this loop, but I&#x27;m guessing it&#x27;s not pretty.