This is an automated email from the ASF dual-hosted git repository. nickva pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/couchdb-jiffy.git
commit c77f342ab89182c06a4bfe064fa3d365d7d26bd5 Author: Nick Vatamaniuc <[email protected]> AuthorDate: Wed Apr 15 23:12:57 2026 -0400 Use branch predictors for encoding Branch predictors are compiler hints to structure the code such that some code is more/less likely to be speculatively executed. This is a technique I noticed in QuickJS code and figured it's worth trying here. This turns out to help in encoding more than decoding. I guess encoding is more "branchy". The improvements are not huge, just a few percentage points, but they are consistent for the majority of encoding benchmarks and with both GCC (Debian) and Clang (MacOS) compilers: Sample of results from Debian 13/GCC ``` ===== With input Encode Pokedex ===== Name ips average deviation median 99th % jiffy (branch-predictor) 1.87 K 535.83 us +/-10.10% 541.35 us 674.64 us jiffy (master) 1.70 K 589.32 us +/-11.87% 579.90 us 783.70 us Comparison: jiffy (branch-predictor) 1.87 K jiffy (master) 1.70 K - 1.10x slower +53.49 us ===== With input Encode Semanticscholar Corpus ===== Name ips average deviation median 99th % jiffy (master) 20.13 49.68 ms +/-4.94% 49.70 ms 58.81 ms jiffy (branch-predictor) 19.37 51.64 ms +/-11.48% 50.40 ms 62.91 ms Comparison: jiffy (master) 20.13 jiffy (branch-predictor) 19.37 - 1.04x slower +1.95 ms ===== With input Encode Twitter ===== Name ips average deviation median 99th % jiffy (branch-predictor) 272.86 3.66 ms +/-14.99% 3.55 ms 5.15 ms jiffy (master) 252.14 3.97 ms +/-14.50% 3.83 ms 5.18 ms Comparison: jiffy (branch-predictor) 272.86 jiffy (master) 252.14 - 1.08x slower +0.30 ms ===== With input Encode UTF-8 escaped ===== Name ips average deviation median 99th % jiffy (branch-predictor) 8.88 K 112.59 us +/-0.76% 112.45 us 115.26 us jiffy (master) 8.44 K 118.48 us +/-1.41% 118.07 us 124.21 us Comparison: jiffy (branch-predictor) 8.88 K jiffy (master) 8.44 K - 1.05x slower +5.89 us ===== With input Encode UTF-8 unescaped ===== Name ips average deviation median 99th % jiffy (branch-predictor) 8.87 K 112.73 us +/-0.72% 112.58 us 115.29 us jiffy (master) 8.48 K 117.87 us +/-0.67% 117.84 us 120.02 us Comparison: jiffy (branch-predictor) 8.87 K jiffy (master) 8.48 K - 1.05x slower +5.14 us ``` Sample of results from MacOS (Clang) ``` ===== With input Encode Pokedex ===== Name ips average deviation median 99th % jiffy (branch-predictor) 1.81 K 551.89 us +/-11.53% 545.92 us 734.93 us jiffy (master) 1.70 K 587.50 us +/-11.49% 580.93 us 779.05 us Comparison: jiffy (branch-predictor) 1.81 K jiffy (master) 1.70 K - 1.06x slower +35.61 us ===== With input Encode Semanticscholar Corpus ===== Name ips average deviation median 99th % jiffy (master) 19.34 51.70 ms +/-9.42% 50.46 ms 64.43 ms jiffy (branch-predictor) 19.33 51.73 ms +/-12.60% 50.31 ms 71.43 ms Comparison: jiffy (master) 19.34 jiffy (branch-predictor) 19.33 - 1.00x slower +0.0301 ms ===== With input Encode Twitter ===== Name ips average deviation median 99th % jiffy (branch-predictor) 274.06 3.65 ms +/-14.39% 3.52 ms 4.81 ms jiffy (master) 256.28 3.90 ms +/-14.25% 3.78 ms 5.11 ms Comparison: jiffy (branch-predictor) 274.06 jiffy (master) 256.28 - 1.07x slower +0.25 ms ===== With input Encode UTF-8 escaped ===== Name ips average deviation median 99th % jiffy (branch-predictor) 8.89 K 112.47 us +/-0.77% 112.32 us 115.13 us jiffy (master) 8.47 K 118.05 us +/-0.79% 118.02 us 120.57 us Comparison: jiffy (branch-predictor) 8.89 K jiffy (master) 8.47 K - 1.05x slower +5.58 us ===== With input Encode UTF-8 unescaped ===== Name ips average deviation median 99th % jiffy (branch-predictor) 8.86 K 112.90 us +/-0.90% 112.67 us 116.13 us jiffy (master) 8.48 K 117.95 us +/-0.72% 117.92 us 120.21 us Comparison: jiffy (branch-predictor) 8.86 K jiffy (master) 8.48 K - 1.04x slower +5.05 us ``` --- c_src/encoder.c | 16 ++++++++-------- c_src/jiffy.h | 9 +++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/c_src/encoder.c b/c_src/encoder.c index 310ebe2..a2a0108 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -261,12 +261,12 @@ enc_ensure(Encoder* e, size_t req) { size_t new_size = BIN_INC_SIZE; - if(e->have_buffer) { - if(req < (e->buffer.size - e->i)) { + if(JIFFY_LIKELY(e->have_buffer)) { + if(JIFFY_LIKELY(req < (e->buffer.size - e->i))) { return 1; } - if(!enc_flush(e)) { + if(JIFFY_UNLIKELY(!enc_flush(e))) { return 0; } @@ -390,9 +390,9 @@ enc_atom(Encoder* e, ERL_NIF_TERM val) return 0; } - if(enc_special_character(e, data[i])) { + if(JIFFY_UNLIKELY(enc_special_character(e, data[i]))) { i++; - } else if(data[i] < 0x80) { + } else if(JIFFY_LIKELY(data[i] < 0x80)) { // Scan ahead for plain ASCII chars which don't need escaping. // Since optionally users could escape forward slashes, too, we // stop on them as well @@ -511,12 +511,12 @@ enc_string(Encoder* e, ERL_NIF_TERM val) } memcpy(&(e->p[e->i]), &data[start], run); e->i += run; - } else if(data[i] >= 0x80) { + } else if(JIFFY_UNLIKELY(data[i] >= 0x80)) { ulen = utf8_validate(&(data[i]), size - i); - if (ulen == 0) { + if (JIFFY_UNLIKELY(ulen == 0)) { return 0; - } else if (e->uescape) { + } else if (JIFFY_UNLIKELY(e->uescape)) { uval = utf8_to_unicode(&(data[i]), size-i); if(uval < 0) { return 0; diff --git a/c_src/jiffy.h b/c_src/jiffy.h index 252d2fc..082b62c 100644 --- a/c_src/jiffy.h +++ b/c_src/jiffy.h @@ -23,6 +23,15 @@ #define inline __inline #endif +// These are to help the branch predictor +#if defined(__GNUC__) || defined(__clang__) + #define JIFFY_LIKELY(x) __builtin_expect(!!(x), 1) + #define JIFFY_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JIFFY_LIKELY(x) (x) + #define JIFFY_UNLIKELY(x) (x) +#endif + typedef struct { ERL_NIF_TERM atom_ok; ERL_NIF_TERM atom_error;
