summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorScott Myron <[email protected]>2025-04-28 07:57:10 -0500
committerJean Boussier <[email protected]>2025-04-30 08:12:41 +0200
commita3ec53bbb0337121d3518d069516bb7b3a795e96 ()
tree81d1249bfcf7d304668cabb94750398c041f9abd /ext
parent7f0c6d30d3d42a9d9ee9ab79e2acd86baa9184f4 (diff)
[ruby/json] Introduce ARM Neon and SSE2 SIMD.
(https://.com/ruby/json/pull/743) See the pull request for the long development history: https://.com/ruby/json/pull/743 ``` == Encoding activitypub.json (52595 bytes) ruby 3.4.2 (2025-02-15 revision https://.com/ruby/json/commit/d2930f8e7a) +YJIT +PRISM [arm64-darwin24] Warming up -------------------------------------- after 2.913k i/100ms Calculating ------------------------------------- after 29.377k (± 2.0%) i/s (34.04 μs/i) - 148.563k in 5.059169s Comparison: before: 23314.1 i/s after: 29377.3 i/s - 1.26x faster == Encoding citm_catalog.json (500298 bytes) ruby 3.4.2 (2025-02-15 revision https://.com/ruby/json/commit/d2930f8e7a) +YJIT +PRISM [arm64-darwin24] Warming up -------------------------------------- after 152.000 i/100ms Calculating ------------------------------------- after 1.569k (± 0.8%) i/s (637.49 μs/i) - 7.904k in 5.039001s Comparison: before: 1485.6 i/s after: 1568.7 i/s - 1.06x faster == Encoding twitter.json (466906 bytes) ruby 3.4.2 (2025-02-15 revision https://.com/ruby/json/commit/d2930f8e7a) +YJIT +PRISM [arm64-darwin24] Warming up -------------------------------------- after 309.000 i/100ms Calculating ------------------------------------- after 3.115k (± 3.1%) i/s (321.01 μs/i) - 15.759k in 5.063776s Comparison: before: 2508.3 i/s after: 3115.2 i/s - 1.24x faster ``` https://.com/ruby/json/commit/49003523da
-rw-r--r--ext/json/generator/extconf.rb31
-rw-r--r--ext/json/generator/generator.c366
-rw-r--r--ext/json/generator/simd.h112
3 files changed, 497 insertions, 12 deletions
@@ -6,5 +6,36 @@ if RUBY_ENGINE == 'truffleruby'
else
append_cflags("-std=c99")
$defs << "-DJSON_GENERATOR"
create_makefile 'json/ext/generator'
end
@@ -5,6 +5,8 @@
#include <math.h>
#include <ctype.h>
/* ruby api and some helpers */
typedef struct JSON_Generator_StateStruct {
@@ -109,12 +111,40 @@ typedef struct _search_state {
const char *end;
const char *cursor;
FBuffer *buffer;
} search_state;
-static inline void search_flush(search_state *search)
-{
- fbuffer_append(search->buffer, search->cursor, search->ptr - search->cursor);
- search->cursor = search->ptr;
}
static const unsigned char escape_table_basic[256] = {
@@ -130,6 +160,8 @@ static const unsigned char escape_table_basic[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static inline unsigned char search_escape_basic(search_state *search)
{
while (search->ptr < search->end) {
@@ -144,7 +176,8 @@ static inline unsigned char search_escape_basic(search_state *search)
return 0;
}
-static inline void escape_UTF8_char_basic(search_state *search) {
const unsigned char ch = (unsigned char)*search->ptr;
switch (ch) {
case '"': fbuffer_append(search->buffer, "\\\"", 2); break;
@@ -156,11 +189,15 @@ static inline void escape_UTF8_char_basic(search_state *search) {
case '\r': fbuffer_append(search->buffer, "\\r", 2); break;
case '\t': fbuffer_append(search->buffer, "\\t", 2); break;
default: {
- const char *hexdig = "0123456789abcdef";
- char scratch[6] = { '\\', 'u', '0', '0', 0, 0 };
- scratch[4] = hexdig[(ch >> 4) & 0xf];
- scratch[5] = hexdig[ch & 0xf];
- fbuffer_append(search->buffer, scratch, 6);
break;
}
}
@@ -186,12 +223,13 @@ static inline void escape_UTF8_char_basic(search_state *search) {
*/
static inline void convert_UTF8_to_JSON(search_state *search)
{
- while (search_escape_basic(search)) {
escape_UTF8_char_basic(search);
}
}
-static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) {
const unsigned char ch = (unsigned char)*search->ptr;
switch (ch_len) {
case 1: {
@@ -227,6 +265,285 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len)
search->cursor = (search->ptr += ch_len);
}
static const unsigned char script_safe_escape_table[256] = {
// ASCII Control Characters
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
@@ -990,6 +1307,12 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat
search.cursor = search.ptr;
search.end = search.ptr + len;
switch(rb_enc_str_coderange(obj)) {
case ENC_CODERANGE_7BIT:
case ENC_CODERANGE_VALID:
@@ -1853,4 +2176,23 @@ void Init_generator(void)
binary_encindex = rb_ascii8bit_encindex();
rb_require("json/ext/generator/state");
}
@@ -0,0 +1,112 @@