[ruby/json] Allocate the initial generator buffer on the stack
Ref: https://github.com/ruby/json/issues/655 Followup: https://github.com/ruby/json/issues/657 Assuming the generator might be used for fairly small documents we can start with a reasonable buffer size of the stack, and if we outgrow it, we can spill on the heap. In a way this is optimizing for micro-benchmarks, but there are valid use case for fiarly small JSON document in actual real world scenarios, so trashing the GC less in such case make sense. Before: ``` ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23] Warming up -------------------------------------- Oj 518.700k i/100ms JSON reuse 483.370k i/100ms Calculating ------------------------------------- Oj 5.722M (± 1.8%) i/s (174.76 ns/i) - 29.047M in 5.077823s JSON reuse 5.278M (± 1.5%) i/s (189.46 ns/i) - 26.585M in 5.038172s Comparison: Oj: 5722283.8 i/s JSON reuse: 5278061.7 i/s - 1.08x slower ``` After: ``` ruby 3.3.4 (2024-07-09 revision https://github.com/ruby/json/commit/be1089c8ec) +YJIT [arm64-darwin23] Warming up -------------------------------------- Oj 517.837k i/100ms JSON reuse 548.871k i/100ms Calculating ------------------------------------- Oj 5.693M (± 1.6%) i/s (175.65 ns/i) - 28.481M in 5.004056s JSON reuse 5.855M (± 1.2%) i/s (170.80 ns/i) - 29.639M in 5.063004s Comparison: Oj: 5692985.6 i/s JSON reuse: 5854857.9 i/s - 1.03x faster ``` https://github.com/ruby/json/commit/fe607f4806
This commit is contained in:
parent
d329896fb5
commit
59eebeca02
@ -4,13 +4,20 @@
|
|||||||
#include "ruby.h"
|
#include "ruby.h"
|
||||||
#include "ruby/encoding.h"
|
#include "ruby/encoding.h"
|
||||||
|
|
||||||
|
enum fbuffer_type {
|
||||||
|
HEAP = 0,
|
||||||
|
STACK = 1,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct FBufferStruct {
|
typedef struct FBufferStruct {
|
||||||
|
enum fbuffer_type type;
|
||||||
unsigned long initial_length;
|
unsigned long initial_length;
|
||||||
char *ptr;
|
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
unsigned long capa;
|
unsigned long capa;
|
||||||
|
char *ptr;
|
||||||
} FBuffer;
|
} FBuffer;
|
||||||
|
|
||||||
|
#define FBUFFER_STACK_SIZE 512
|
||||||
#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
|
#define FBUFFER_INITIAL_LENGTH_DEFAULT 1024
|
||||||
|
|
||||||
#define FBUFFER_PTR(fb) ((fb)->ptr)
|
#define FBUFFER_PTR(fb) ((fb)->ptr)
|
||||||
@ -35,14 +42,21 @@ static VALUE fbuffer_to_s(FBuffer *fb);
|
|||||||
#define RB_UNLIKELY(expr) expr
|
#define RB_UNLIKELY(expr) expr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void fbuffer_init(FBuffer *fb, unsigned long initial_length)
|
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
|
||||||
{
|
{
|
||||||
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
|
||||||
|
if (stack_buffer) {
|
||||||
|
fb->type = STACK;
|
||||||
|
fb->ptr = stack_buffer;
|
||||||
|
fb->capa = stack_buffer_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fbuffer_free(FBuffer *fb)
|
static void fbuffer_free(FBuffer *fb)
|
||||||
{
|
{
|
||||||
if (fb->ptr) ruby_xfree(fb->ptr);
|
if (fb->ptr && fb->type == HEAP) {
|
||||||
|
ruby_xfree(fb->ptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef JSON_GENERATOR
|
#ifndef JSON_GENERATOR
|
||||||
@ -65,7 +79,14 @@ static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
|
|||||||
for (required = fb->capa; requested > required - fb->len; required <<= 1);
|
for (required = fb->capa; requested > required - fb->len; required <<= 1);
|
||||||
|
|
||||||
if (required > fb->capa) {
|
if (required > fb->capa) {
|
||||||
REALLOC_N(fb->ptr, char, required);
|
if (fb->type == STACK) {
|
||||||
|
const char *old_buffer = fb->ptr;
|
||||||
|
fb->ptr = ALLOC_N(char, required);
|
||||||
|
fb->type = HEAP;
|
||||||
|
MEMCPY(fb->ptr, old_buffer, char, fb->len);
|
||||||
|
} else {
|
||||||
|
REALLOC_N(fb->ptr, char, required);
|
||||||
|
}
|
||||||
fb->capa = required;
|
fb->capa = required;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -954,8 +954,9 @@ static VALUE cState_partial_generate(VALUE self, VALUE obj, void (*func)(FBuffer
|
|||||||
{
|
{
|
||||||
GET_STATE(self);
|
GET_STATE(self);
|
||||||
|
|
||||||
|
char stack_buffer[FBUFFER_STACK_SIZE];
|
||||||
FBuffer buffer = {0};
|
FBuffer buffer = {0};
|
||||||
fbuffer_init(&buffer, state->buffer_initial_length);
|
fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
|
||||||
|
|
||||||
struct generate_json_data data = {
|
struct generate_json_data data = {
|
||||||
.buffer = &buffer,
|
.buffer = &buffer,
|
||||||
|
@ -54,6 +54,7 @@ typedef struct JSON_Generator_StateStruct {
|
|||||||
JSON_Generator_State *state; \
|
JSON_Generator_State *state; \
|
||||||
GET_STATE_TO(self, state)
|
GET_STATE_TO(self, state)
|
||||||
|
|
||||||
|
|
||||||
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
|
static VALUE mHash_to_json(int argc, VALUE *argv, VALUE self);
|
||||||
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
|
static VALUE mArray_to_json(int argc, VALUE *argv, VALUE self);
|
||||||
#ifdef RUBY_INTEGER_UNIFICATION
|
#ifdef RUBY_INTEGER_UNIFICATION
|
||||||
|
@ -2159,7 +2159,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|||||||
{
|
{
|
||||||
JSON_Parser *json;
|
JSON_Parser *json;
|
||||||
VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
|
VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
|
||||||
fbuffer_init(&json->fbuffer, 0);
|
fbuffer_stack_init(&json->fbuffer, 0, NULL, 0);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -919,7 +919,7 @@ static VALUE cJSON_parser_s_allocate(VALUE klass)
|
|||||||
{
|
{
|
||||||
JSON_Parser *json;
|
JSON_Parser *json;
|
||||||
VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
|
VALUE obj = TypedData_Make_Struct(klass, JSON_Parser, &JSON_Parser_type, json);
|
||||||
fbuffer_init(&json->fbuffer, 0);
|
fbuffer_stack_init(&json->fbuffer, 0, NULL, 0);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user