summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2025-11-04 09:08:05 +0100
committergit <svn-admin@ruby-lang.org>2025-11-04 08:49:57 +0000
commit7c924013630699321d51438f3754b291d8d4a37e (patch)
treef34b08b243f563b97caabab0b053295daf3fa4a6
parent157ae44b1e5f44d04e3dcb5cad4653e0f56e889d (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.c43
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;