diff options
| author | Jean Boussier <jean.boussier@gmail.com> | 2025-11-04 09:08:05 +0100 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2025-11-04 08:49:57 +0000 |
| commit | 7c924013630699321d51438f3754b291d8d4a37e (patch) | |
| tree | f34b08b243f563b97caabab0b053295daf3fa4a6 | |
| parent | 157ae44b1e5f44d04e3dcb5cad4653e0f56e889d (diff) | |
[ruby/json] Micro-optimize `rstring_cache_fetch`
Closes: https://github.com/ruby/json/pull/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 https://github.com/ruby/json/commit/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 https://github.com/ruby/json/commit/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 https://github.com/ruby/json/commit/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 https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24]
Comparison:
before: 276.8 i/s
after: 276.4 i/s - same-ish: difference falls within error
```
https://github.com/ruby/json/commit/a67d1a1af4
Co-Authored-By: Scott Myron <samyron@gmail.com>
| -rw-r--r-- | ext/json/parser/parser.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 0f4d31097b..3d88a399e7 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -84,17 +84,52 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring cache->entries[index] = rstring; } -static inline int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +#if JSON_CPU_LITTLE_ENDIAN_64BITS && defined(__has_builtin) && __has_builtin(__builtin_bswap64) +static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length) { - long rstring_length = RSTRING_LEN(rstring); + // The libc memcmp has numerous complex optimizations, but in this particular case, + // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to + // inline a simpler memcmp outperforms calling the libc version. + long i = 0; + + for (; i + 8 <= length; i += 8) { + uint64_t a, b; + memcpy(&a, str + i, 8); + memcpy(&b, rptr + i, 8); + if (a != b) { + a = __builtin_bswap64(a); + b = __builtin_bswap64(b); + return (a < b) ? -1 : 1; + } + } + + for (; i < length; i++) { + if (str[i] != rptr[i]) { + return (str[i] < rptr[i]) ? -1 : 1; + } + } + + return 0; +} +#else +#define rstring_cache_memcmp memcmp +#endif + +static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +{ + const char *rstring_ptr; + long rstring_length; + + RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); + if (length == rstring_length) { - return memcmp(str, RSTRING_PTR(rstring), length); + return rstring_cache_memcmp(str, rstring_ptr, length); } else { return (int)(length - rstring_length); } } -static VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) +static ALWAYS_INLINE() VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) { int low = 0; int high = cache->length - 1; |
