From f7d7a248efaa11cd86134ac0670905229584bc59 Mon Sep 17 00:00:00 2001 From: eno Date: Sat, 31 May 2025 14:55:13 +0200 Subject: [PATCH] Speed up String encoding on CPUs without SIMD hardware support This change tests for the presence of characters to be escaped on CPUs without special SIMD instructions. We are testing 8 chars in one round, following a code excerpt from https://lemire.me/blog/2025/04/13/detect-control-characters-quotes-and-backslashes-efficiently-using-swar/ For strings that don't require escapes I have seen speedups of around 15% with this change. --- ext/json/ext/generator/generator.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index f7690a23e..7113ecf2a 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -162,8 +162,25 @@ static const unsigned char escape_table_basic[256] = { static unsigned char (*search_escape_basic_impl)(search_state *); +inline bool has_json_escapable_byte(uint64_t x) { + uint64_t is_ascii = 0x8080808080808080ULL & ~x; + uint64_t xor2 = x ^ 0x0202020202020202ULL; + uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL; + uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL; + uint64_t eq92 = (sub92 - 0x0101010101010101ULL); + return ((lt32_or_eq34 | eq92) & is_ascii) != 0; +} + static inline unsigned char search_escape_basic(search_state *search) { + while (search->ptr <= search->end - 8) { + uint64_t* pi = (uint64_t*)(search->ptr); + if(has_json_escapable_byte(*pi)) { + break; + } + search->ptr += 8; + } + while (search->ptr < search->end) { if (RB_UNLIKELY(escape_table_basic[(const unsigned char)*search->ptr])) { search_flush(search);