Skip to content

Commit a67d1a1

Browse files
byrootsamyron
andcommitted
Micro-optimize rstring_cache_fetch
Closes: #888 - Mark it as `inline`. - Use `RSTRING_GETMEM`, instead of `RSTRING_LEN` and `RSTRING_PTR`. - Use an inlinable version of `memcmp`. ``` == Parsing activitypub.json (58160 bytes) ruby 3.4.6 (2025-09-16 revision dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 11766.6 i/s after: 12272.1 i/s - 1.04x faster == Parsing twitter.json (567916 bytes) ruby 3.4.6 (2025-09-16 revision dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 1333.2 i/s after: 1422.0 i/s - 1.07x faster == Parsing citm_catalog.json (1727030 bytes) ruby 3.4.6 (2025-09-16 revision dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 656.3 i/s after: 673.1 i/s - 1.03x faster == Parsing float parsing (2251051 bytes) ruby 3.4.6 (2025-09-16 revision dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 276.8 i/s after: 276.4 i/s - same-ish: difference falls within error ``` Co-Authored-By: Scott Myron <samyron@gmail.com>
1 parent ddad00b commit a67d1a1

File tree

1 file changed

+39
-4
lines changed

1 file changed

+39
-4
lines changed

ext/json/ext/parser/parser.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,52 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring
8484
cache->entries[index] = rstring;
8585
}
8686

87-
static inline int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
87+
#if JSON_CPU_LITTLE_ENDIAN_64BITS && defined(__has_builtin) && __has_builtin(__builtin_bswap64)
88+
static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length)
8889
{
89-
long rstring_length = RSTRING_LEN(rstring);
90+
// The libc memcmp has numerous complex optimizations, but in this particular case,
91+
// we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to
92+
// inline a simpler memcmp outperforms calling the libc version.
93+
long i = 0;
94+
95+
for (; i + 8 <= length; i += 8) {
96+
uint64_t a, b;
97+
memcpy(&a, str + i, 8);
98+
memcpy(&b, rptr + i, 8);
99+
if (a != b) {
100+
a = __builtin_bswap64(a);
101+
b = __builtin_bswap64(b);
102+
return (a < b) ? -1 : 1;
103+
}
104+
}
105+
106+
for (; i < length; i++) {
107+
if (str[i] != rptr[i]) {
108+
return (str[i] < rptr[i]) ? -1 : 1;
109+
}
110+
}
111+
112+
return 0;
113+
}
114+
#else
115+
#define rstring_cache_memcmp memcmp
116+
#endif
117+
118+
static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
119+
{
120+
const char *rstring_ptr;
121+
long rstring_length;
122+
123+
RSTRING_GETMEM(rstring, rstring_ptr, rstring_length);
124+
90125
if (length == rstring_length) {
91-
return memcmp(str, RSTRING_PTR(rstring), length);
126+
return rstring_cache_memcmp(str, rstring_ptr, length);
92127
} else {
93128
return (int)(length - rstring_length);
94129
}
95130
}
96131

97-
static VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
132+
static ALWAYS_INLINE() VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length)
98133
{
99134
int low = 0;
100135
int high = cache->length - 1;

0 commit comments

Comments
 (0)