3 ms·
Ah, thanks for the reply. I understand you better now, and for the most part I agree. Taking out the early exit for ASCII can speed up the frequently-changing c
by zwegner 7y ago
Ah, thanks for the reply. I understand you better now, and for the most part I agree. Taking out the early exit for ASCII can speed up the frequently-changing case to avoid the misprediction penalty, as long as sufficiently many chunks of input need to take the long validation path.
Thinking about this more, I think there's at least one way that an adaptive approach might be beneficial without too much extra complexity: keeping two copies of the main kernel for ASCII-checking and non-ASCII checking. It should be pretty cheap to add a counter that is incremented or decremented based on the ASCII-only mask, and split the outer loop into N-byte chunks, with a branch for each chunk determining whether we should take the branchless path or not.
> Right, well the ASCII one is a big one. I haven't looked at the details of your algorithm, but could the core loop be faster if say it never saw any 4-byte sequences, or certain other uncommon things? Then an adaptive approach would use an optimized kernel for that scenario if it was encountered for a while.
I started out thinking this wouldn't really work, but I think there might be some potential here. My initial problem was that even if some work could be saved if there weren't 4 byte sequences, we still need to detect them every time. But, because my algorithm uses lookup tables for error flags which have some free bits, and because the 4 byte sequences can be detected in the same indices that are used for these lookups, we can set another error bit that means "take the 4-byte slow path". Then, when some input fails validation, we only do the work there to check whether it's really a failure. This gets complicated, though: first off, the check for the proper number of continuation bytes is before the table lookup, so we'd need to put some logic in there. Secondly, this lookup table gets validation failures one byte later in the stream than the initial byte. So in the case that the 4-byte sequence starts on the last byte of a vector, we'd need to have special handling.
It'd probably be a good idea to also have some hysteresis in this approach too... So overall, I think there might be some nice gains from adaptive behavior. There are two big concerns that make me skeptical, though: code size/I$ pressure, and code complexity. While code size wouldn't be much of an issue in microbenchmarks, in real applications it matters a lot more--I don't want the size overhead to get too big. Right now the full validation algorithm is about 600 bytes of code for AVX2, I'd rather not make that explode by a large factor by adding several specializations. And I'm already reaching the limits of what little generic programming can be done in the C preprocessor... This sort of specialization is really better done with C++ templates (or maybe Rust or something). I had wanted to keep this a pure-C library, but maybe C++ might be better.
Hope some of that makes sense... I'm mostly thinking out loud here. In any case, you've given me a lot to think about, so thanks very much!
- BeeOnRope 7y agoThanks for the reply. I put some more thoughts on github.