summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2025-11-01 12:15:04 +0100
committergit <svn-admin@ruby-lang.org>2025-11-01 12:00:01 +0000
commited7229eac8a5678211be8f1468af778d7beebf5c (patch)
treeddb670e2afc54772cb6ecab033114116e99be445
parent94287b1e18edee04a0fd646fde84fe1178ce46d1 (diff)
[ruby/json] parser.c: Use SWAR to skip consecutive spaces
Closes: https://github.com/ruby/json/pull/881 If we encounter a newline, it is likely that the document is pretty printed, hence that the newline is followed by multiple spaces. In such case we can use SWAR to count up to eight consecutive spaces at once. ``` == Parsing activitypub.json (58160 bytes) ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24] Warming up -------------------------------------- after 1.118k i/100ms Calculating ------------------------------------- after 11.223k (± 0.7%) i/s (89.10 μs/i) - 57.018k in 5.080522s Comparison: before: 10834.4 i/s after: 11223.4 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] Warming up -------------------------------------- after 118.000 i/100ms Calculating ------------------------------------- after 1.188k (± 1.0%) i/s (841.62 μs/i) - 6.018k in 5.065355s Comparison: before: 1094.8 i/s after: 1188.2 i/s - 1.09x 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] Warming up -------------------------------------- after 58.000 i/100ms Calculating ------------------------------------- after 570.506 (± 3.7%) i/s (1.75 ms/i) - 2.900k in 5.091529s Comparison: before: 419.6 i/s after: 570.5 i/s - 1.36x 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] Warming up -------------------------------------- after 22.000 i/100ms Calculating ------------------------------------- after 212.010 (± 1.9%) i/s (4.72 ms/i) - 1.078k in 5.086885s Comparison: before: 189.4 i/s after: 212.0 i/s - 1.12x faster ``` https://github.com/ruby/json/commit/b3fd7b26be Co-Authored-By: Scott Myron <samyron@gmail.com>
-rw-r--r--ext/json/parser/parser.c46
-rw-r--r--ext/json/simd/simd.h3
2 files changed, 33 insertions, 16 deletions
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index e591ca2c5a..01234b5a86 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -557,14 +557,6 @@ static uint32_t unescape_unicode(JSON_ParserState *state, const unsigned char *p
static const rb_data_type_t JSON_ParserConfig_type;
-static const bool whitespace[256] = {
- [' '] = 1,
- ['\t'] = 1,
- ['\n'] = 1,
- ['\r'] = 1,
- ['/'] = 1,
-};
-
static void
json_eat_comments(JSON_ParserState *state)
{
@@ -607,12 +599,38 @@ json_eat_comments(JSON_ParserState *state)
static inline void
json_eat_whitespace(JSON_ParserState *state)
{
- unsigned char cursor;
- while (RB_UNLIKELY(whitespace[cursor = (unsigned char)peek(state)])) {
- if (RB_UNLIKELY(cursor == '/')) {
- json_eat_comments(state);
- } else {
- state->cursor++;
+ while (true) {
+ switch (peek(state)) {
+ case ' ':
+ state->cursor++;
+ break;
+ case '\n':
+ state->cursor++;
+
+ // Heuristic: if we see a newline, there is likely consecutive spaces after it.
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ while (rest(state) > 8) {
+ uint64_t chunk;
+ memcpy(&chunk, state->cursor, sizeof(uint64_t));
+ size_t consecutive_spaces = trailing_zeros64(chunk ^ 0x2020202020202020) / CHAR_BIT;
+
+ state->cursor += consecutive_spaces;
+ if (consecutive_spaces != 8) {
+ break;
+ }
+ }
+#endif
+ break;
+ case '\t':
+ case '\r':
+ state->cursor++;
+ break;
+ case '/':
+ json_eat_comments(state);
+ break;
+
+ default:
+ return;
}
}
}
diff --git a/ext/json/simd/simd.h b/ext/json/simd/simd.h
index 3abbdb0209..2aa6c3d046 100644
--- a/ext/json/simd/simd.h
+++ b/ext/json/simd/simd.h
@@ -4,8 +4,6 @@ typedef enum {
SIMD_SSE2
} SIMD_Implementation;
-#ifdef JSON_ENABLE_SIMD
-
#ifdef __clang__
# if __has_builtin(__builtin_ctzll)
# define HAVE_BUILTIN_CTZLL 1
@@ -54,6 +52,7 @@ static inline int trailing_zeros(int input)
#define FORCE_INLINE
#endif
+#ifdef JSON_ENABLE_SIMD
#define SIMD_MINIMUM_THRESHOLD 6