Skip to content

Commit b4cbcf2

Browse files
committed
Use batch APIs to create arrays and hashes
Basically a port of ruby/json#678 Rather than to allocate the container and push elements one by one, we accumulate them on a stack and then use the faster batch APIs to directly create the final container.
1 parent 6bbaa97 commit b4cbcf2

File tree

2 files changed

+145
-64
lines changed

2 files changed

+145
-64
lines changed

ext/msgpack/unpacker.c

Lines changed: 122 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
static int RAW_TYPE_STRING = 256;
3030
static int RAW_TYPE_BINARY = 257;
31-
static int16_t INITIAL_BUFFER_CAPACITY_MAX = SHRT_MAX;
3231

3332
static msgpack_rmem_t s_stack_rmem;
3433

@@ -39,9 +38,98 @@ static inline VALUE rb_hash_new_capa(long capa)
3938
}
4039
#endif
4140

42-
static inline int16_t initial_buffer_size(long size)
41+
/* rvalue_stack functions */
42+
43+
static inline void _msgpack_unpacker_rvalue_stack_free(msgpack_rvalue_stack_t* value_stack) {
44+
switch (value_stack->type) {
45+
case STACK_TYPE_UNALLOCATED:
46+
break;
47+
case STACK_TYPE_RMEM:
48+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
49+
rb_bug("Failed to free an rmem pointer, memory leak?");
50+
}
51+
break;
52+
case STACK_TYPE_HEAP:
53+
xfree(value_stack->data);
54+
break;
55+
}
56+
memset(value_stack, 0, sizeof(msgpack_rvalue_stack_t));
57+
}
58+
59+
static void _msgpack_unpacker_rvalue_stack_grow(msgpack_rvalue_stack_t* value_stack) {
60+
switch (value_stack->type) {
61+
case STACK_TYPE_UNALLOCATED: {
62+
value_stack->data = msgpack_rmem_alloc(&s_stack_rmem);
63+
value_stack->capacity = MSGPACK_RMEM_PAGE_SIZE / sizeof(VALUE);
64+
value_stack->type = STACK_TYPE_RMEM;
65+
break;
66+
}
67+
case STACK_TYPE_RMEM: {
68+
size_t new_capacity = value_stack->capacity * 2;
69+
VALUE *new_ptr = ALLOC_N(VALUE, new_capacity);
70+
MEMCPY(new_ptr, value_stack->data, VALUE, value_stack->depth);
71+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
72+
rb_bug("Failed to free an rmem pointer, memory leak?");
73+
}
74+
value_stack->type = STACK_TYPE_HEAP;
75+
value_stack->data = new_ptr;
76+
value_stack->capacity = new_capacity;
77+
break;
78+
}
79+
case STACK_TYPE_HEAP: {
80+
size_t new_capacity = value_stack->capacity * 2;
81+
REALLOC_N(value_stack->data, VALUE, new_capacity);
82+
value_stack->capacity = new_capacity;
83+
break;
84+
}
85+
}
86+
}
87+
88+
static inline void _msgpack_unpacker_rvalue_stack_push(msgpack_unpacker_t* uk, VALUE value) {
89+
size_t free_slots = uk->value_stack.capacity - uk->value_stack.depth;
90+
91+
if (RB_UNLIKELY(free_slots == 0)) {
92+
_msgpack_unpacker_rvalue_stack_grow(&uk->value_stack);
93+
free_slots = uk->value_stack.capacity - uk->value_stack.depth;
94+
}
95+
96+
RB_OBJ_WRITE(uk->self, &uk->value_stack.data[uk->value_stack.depth++], value);
97+
}
98+
99+
// static inline VALUE _msgpack_unpacker_rvalue_stack_top(msgpack_unpacker_t* uk)
100+
// {
101+
// return uk->value_stack.data[uk->value_stack.depth - 1];
102+
// }
103+
104+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_array(msgpack_unpacker_t* uk, size_t length) {
105+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - length];
106+
107+
VALUE array = rb_ary_new_from_values(length, data);
108+
109+
RB_OBJ_WRITE(uk->self, data, array);
110+
uk->value_stack.depth -= (length - 1);
111+
112+
return RB_GC_GUARD(array);
113+
}
114+
115+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_hash(msgpack_unpacker_t* uk, size_t items_count) {
116+
size_t length = items_count / 2;
117+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - items_count];
118+
119+
VALUE hash = rb_hash_new_capa(length);
120+
rb_hash_bulk_insert(items_count, data, hash);
121+
122+
RB_OBJ_WRITE(uk->self, data, hash);
123+
uk->value_stack.depth -= (items_count - 1);
124+
125+
return RB_GC_GUARD(hash);
126+
}
127+
128+
void msgpack_unpacker_mark_rvalue_stack(msgpack_rvalue_stack_t* value_stack)
43129
{
44-
return (size > INITIAL_BUFFER_CAPACITY_MAX) ? INITIAL_BUFFER_CAPACITY_MAX : size;
130+
if (value_stack->data) {
131+
rb_gc_mark_locations(value_stack->data, value_stack->data + value_stack->depth);
132+
}
45133
}
46134

47135
void msgpack_unpacker_static_init(void)
@@ -88,27 +176,15 @@ static inline void _msgpack_unpacker_free_stack(msgpack_unpacker_stack_t* stack)
88176
void _msgpack_unpacker_destroy(msgpack_unpacker_t* uk)
89177
{
90178
_msgpack_unpacker_free_stack(uk->stack);
179+
_msgpack_unpacker_rvalue_stack_free(&uk->value_stack);
91180
msgpack_buffer_destroy(UNPACKER_BUFFER_(uk));
92181
}
93182

94-
void msgpack_unpacker_mark_stack(msgpack_unpacker_stack_t* stack)
95-
{
96-
while (stack) {
97-
msgpack_unpacker_stack_entry_t* s = stack->data;
98-
msgpack_unpacker_stack_entry_t* send = stack->data + stack->depth;
99-
for(; s < send; s++) {
100-
rb_gc_mark(s->object);
101-
rb_gc_mark(s->key);
102-
}
103-
stack = stack->parent;
104-
}
105-
}
106-
107183
void msgpack_unpacker_mark(msgpack_unpacker_t* uk)
108184
{
109185
rb_gc_mark(uk->last_object);
110186
rb_gc_mark(uk->reading_raw);
111-
msgpack_unpacker_mark_stack(uk->stack);
187+
msgpack_unpacker_mark_rvalue_stack(&uk->value_stack);
112188
/* See MessagePack_Buffer_wrap */
113189
/* msgpack_buffer_mark(UNPACKER_BUFFER_(uk)); */
114190
rb_gc_mark(uk->buffer_ref);
@@ -160,13 +236,15 @@ static inline int object_complete(msgpack_unpacker_t* uk, VALUE object)
160236
}
161237

162238
uk->last_object = object;
239+
_msgpack_unpacker_rvalue_stack_push(uk, object);
163240
reset_head_byte(uk);
164241
return PRIMITIVE_OBJECT_COMPLETE;
165242
}
166243

167244
static inline int object_complete_symbol(msgpack_unpacker_t* uk, VALUE object)
168245
{
169246
uk->last_object = object;
247+
_msgpack_unpacker_rvalue_stack_push(uk, object);
170248
reset_head_byte(uk);
171249
return PRIMITIVE_OBJECT_COMPLETE;
172250
}
@@ -204,7 +282,7 @@ static inline msgpack_unpacker_stack_entry_t* _msgpack_unpacker_stack_entry_top(
204282
return &uk->stack->data[uk->stack->depth-1];
205283
}
206284

207-
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count, VALUE object)
285+
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count)
208286
{
209287
reset_head_byte(uk);
210288

@@ -214,22 +292,20 @@ static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stac
214292

215293
msgpack_unpacker_stack_entry_t* next = &uk->stack->data[uk->stack->depth];
216294
next->count = count;
295+
next->size = count;
217296
next->type = type;
218-
next->object = object;
219-
next->key = Qnil;
220-
221297
uk->stack->depth++;
222298
return PRIMITIVE_CONTAINER_START;
223299
}
224300

225-
static inline VALUE msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
301+
static inline size_t msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
226302
{
227303
return --uk->stack->depth;
228304
}
229305

230306
static inline bool msgpack_unpacker_stack_is_empty(msgpack_unpacker_t* uk)
231307
{
232-
return uk->stack->depth == 0;
308+
return uk->stack->depth == 0 || _msgpack_unpacker_stack_entry_top(uk)->type == STACK_TYPE_RECURSIVE;
233309
}
234310

235311
#ifdef USE_CASE_RANGE
@@ -259,9 +335,7 @@ static inline bool is_reading_map_key(msgpack_unpacker_t* uk)
259335
{
260336
if(uk->stack->depth > 0) {
261337
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
262-
if(top->type == STACK_TYPE_MAP_KEY) {
263-
return true;
264-
}
338+
return top->type == STACK_TYPE_MAP && (top->count % 2 == 0);
265339
}
266340
return false;
267341
}
@@ -312,14 +386,9 @@ static inline int read_raw_body_begin(msgpack_unpacker_t* uk, int raw_type)
312386
reset_head_byte(uk);
313387
uk->reading_raw_remaining = 0;
314388

315-
msgpack_unpacker_stack_t* child_stack = _msgpack_unpacker_new_stack();
316-
child_stack->parent = uk->stack;
317-
uk->stack = child_stack;
318-
389+
_msgpack_unpacker_stack_push(uk, STACK_TYPE_RECURSIVE, 1);
319390
obj = rb_proc_call_with_block(proc, 1, &uk->self, Qnil);
320-
321-
uk->stack = child_stack->parent;
322-
_msgpack_unpacker_free_stack(child_stack);
391+
msgpack_unpacker_stack_pop(uk);
323392

324393
return object_complete(uk, obj);
325394
}
@@ -382,14 +451,14 @@ static int read_primitive(msgpack_unpacker_t* uk)
382451
if(count == 0) {
383452
return object_complete(uk, rb_ary_new());
384453
}
385-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
454+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
386455

387456
SWITCH_RANGE(b, 0x80, 0x8f) // FixMap
388457
int count = b & 0x0f;
389458
if(count == 0) {
390459
return object_complete(uk, rb_hash_new());
391460
}
392-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
461+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
393462

394463
SWITCH_RANGE(b, 0xc0, 0xdf) // Variable
395464
switch(b) {
@@ -612,7 +681,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
612681
if(count == 0) {
613682
return object_complete(uk, rb_ary_new());
614683
}
615-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
684+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
616685
}
617686

618687
case 0xdd: // array 32
@@ -622,7 +691,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
622691
if(count == 0) {
623692
return object_complete(uk, rb_ary_new());
624693
}
625-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
694+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
626695
}
627696

628697
case 0xde: // map 16
@@ -632,7 +701,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
632701
if(count == 0) {
633702
return object_complete(uk, rb_hash_new());
634703
}
635-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
704+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
636705
}
637706

638707
case 0xdf: // map 32
@@ -642,7 +711,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
642711
if(count == 0) {
643712
return object_complete(uk, rb_hash_new());
644713
}
645-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
714+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
646715
}
647716

648717
default:
@@ -730,28 +799,23 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
730799
container_completed:
731800
{
732801
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
733-
switch(top->type) {
734-
case STACK_TYPE_ARRAY:
735-
rb_ary_push(top->object, uk->last_object);
736-
break;
737-
case STACK_TYPE_MAP_KEY:
738-
top->key = uk->last_object;
739-
top->type = STACK_TYPE_MAP_VALUE;
740-
break;
741-
case STACK_TYPE_MAP_VALUE:
742-
if(uk->symbolize_keys && rb_type(top->key) == T_STRING) {
743-
/* here uses rb_str_intern instead of rb_intern so that Ruby VM can GC unused symbols */
744-
rb_hash_aset(top->object, rb_str_intern(top->key), uk->last_object);
745-
} else {
746-
rb_hash_aset(top->object, top->key, uk->last_object);
747-
}
748-
top->type = STACK_TYPE_MAP_KEY;
749-
break;
750-
}
751802
size_t count = --top->count;
752803

753804
if(count == 0) {
754-
object_complete(uk, top->object);
805+
switch(top->type) {
806+
case STACK_TYPE_ARRAY:
807+
_msgpack_unpacker_rvalue_stack_create_array(uk, top->size);
808+
break;
809+
case STACK_TYPE_MAP:
810+
_msgpack_unpacker_rvalue_stack_create_hash(uk, top->size);
811+
break;
812+
case STACK_TYPE_RECURSIVE:
813+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
814+
return PRIMITIVE_OBJECT_COMPLETE;
815+
break;
816+
}
817+
818+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
755819
if(msgpack_unpacker_stack_pop(uk) <= target_stack_depth) {
756820
return PRIMITIVE_OBJECT_COMPLETE;
757821
}

ext/msgpack/unpacker.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,42 @@
2626
struct msgpack_unpacker_t;
2727
typedef struct msgpack_unpacker_t msgpack_unpacker_t;
2828
typedef struct msgpack_unpacker_stack_t msgpack_unpacker_stack_t;
29+
typedef struct msgpack_rvalue_stack_t msgpack_rvalue_stack_t;
30+
31+
enum rvalue_stack_type_t {
32+
STACK_TYPE_UNALLOCATED = 0,
33+
STACK_TYPE_RMEM = 1,
34+
STACK_TYPE_HEAP = 2,
35+
};
36+
37+
struct msgpack_rvalue_stack_t {
38+
enum rvalue_stack_type_t type;
39+
size_t depth;
40+
size_t capacity;
41+
VALUE *data;
42+
};
2943

3044
enum stack_type_t {
3145
STACK_TYPE_ARRAY,
32-
STACK_TYPE_MAP_KEY,
33-
STACK_TYPE_MAP_VALUE,
46+
STACK_TYPE_MAP,
47+
STACK_TYPE_RECURSIVE,
3448
};
3549

3650
typedef struct {
3751
size_t count;
52+
size_t size;
3853
enum stack_type_t type;
39-
VALUE object;
40-
VALUE key;
4154
} msgpack_unpacker_stack_entry_t;
4255

4356
struct msgpack_unpacker_stack_t {
4457
size_t depth;
4558
size_t capacity;
4659
msgpack_unpacker_stack_entry_t *data;
47-
msgpack_unpacker_stack_t *parent;
4860
};
4961

5062
struct msgpack_unpacker_t {
5163
msgpack_buffer_t buffer;
64+
msgpack_rvalue_stack_t value_stack;
5265
msgpack_unpacker_stack_t *stack;
5366
unsigned int head_byte;
5467

@@ -124,9 +137,13 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth);
124137

125138
int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth);
126139

140+
static inline VALUE _msgpack_unpacker_rvalue_stack_pop(msgpack_unpacker_t* uk) {
141+
return uk->value_stack.data[--uk->value_stack.depth];
142+
}
143+
127144
static inline VALUE msgpack_unpacker_get_last_object(msgpack_unpacker_t* uk)
128145
{
129-
return uk->last_object;
146+
return _msgpack_unpacker_rvalue_stack_pop(uk);
130147
}
131148

132149

0 commit comments

Comments
 (0)