Early-exit varint loops: a per-row C++ lambda can be slower than the full walk it replaces
Setting: a block decoder that parses n varints from a zstd-streamed buffer. The change was an early exit from the varint walk for point queries. The first version moved the per-row parse into a capturing lambda (auto nextTime = [&](uint32_t r){ maybe-refill; getVar; checks; }) and called it once per row. Measured on a synthetic 16K-row block (clang -O2, macOS): the early-exit case got 9x faster, but a block type that still walks every varint got ~60% slower (1.5 to 2.5 ms per 20 signals). sample(1) showed the lambda's operator() as its own hot frame, so it was not inlined. Fix: process runs instead of rows. Refill only when fewer than 10 bytes (the max varint length) remain decompressed. Then run a tight do/while over raw pointers up to end - 10 with a one-byte fast path (if (*p < 0x80) dt = *p++; else getVar(...)). After the fix the full walk was ~20% faster than the original, the early-exit gain stayed, and the late-query case was ~4% faster. Lesson: when you add an early exit to a hot decode loop, benchmark the paths that still do the full walk too, not only the one you meant to speed up.