Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions bench/various_appraches_bench.rb
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,15 @@ def self.build_untracked_packer(struct)
RUBY
end

def self.build_untracked_alloc_packer(struct)
packer_body = struct.members.size.times.map { |i| "packer.write(obj[#{i}])" }.join("\n")
eval(<<~RUBY, binding, __FILE__, __LINE__ + 1)
->(obj, packer) {
#{packer_body}
}
RUBY
end

def self.build_tracked_unpacker(struct)
args = struct.members.map { |_m| "unpacker.read" }.join(", ")

Expand Down Expand Up @@ -270,6 +279,16 @@ def self.build_untracked_unpacker(struct)
}
RUBY
end

def self.build_untracked_alloc_unpacker(struct)
args = struct.members.size.times.map { |i| "s[#{i}] = unpacker.read" }.join("\n")
eval(<<~RUBY, binding, __FILE__, __LINE__ + 1)
->(unpacker) {
s = #{struct}.allocate
#{args}
}
RUBY
end
end

module Coders
Expand Down Expand Up @@ -321,6 +340,52 @@ def self.description
end
end

module PlainRubyPooled
class PooledFactory
def initialize(factory)
@packer = factory.packer
@unpacker = factory.unpacker
end

def load(data)
@unpacker.feed(data)
@unpacker.full_unpack
end

def dump(obj)
@packer.write(obj)
@packer.full_pack.freeze
end
end

def self.build_factory
factory = MessagePack::Factory.new
Coders.register_time(factory)

type_id = 0x20
ALL_STRUCTS.each do |struct|
factory.register_type(
type_id,
struct,
packer: CodeGen.build_untracked_alloc_packer(struct),
unpacker: CodeGen.build_untracked_alloc_unpacker(struct),
recursive: true,
)
type_id += 1
end

factory
end

def self.factory
@factory ||= PooledFactory.new(build_factory)
end

def self.description
"A naive pure-Ruby coder without any optimizations to the msgpack-ruby gem, and no ref_tracking, but using smarter initialization."
end
end

module PlainRubyArrays
def self.build_factory
factory = MessagePack::Factory.new
Expand Down
54 changes: 9 additions & 45 deletions ext/msgpack/unpacker.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,57 +489,21 @@ static inline int read_raw_body_begin(msgpack_unpacker_t* uk, int raw_type)
reset_head_byte(uk);
uk->reading_raw_remaining = 0;

/* Get struct members */
VALUE members = rb_struct_s_members(struct_class);
long num_fields = RARRAY_LEN(members);

/* Check if this is a keyword_init struct (Ruby 2.7+) */
VALUE keyword_init = Qfalse;
if (rb_respond_to(struct_class, rb_intern("keyword_init?"))) {
keyword_init = rb_funcall(struct_class, rb_intern("keyword_init?"), 0);
}

/* Push a recursive marker so nested reads don't prematurely return */
_msgpack_unpacker_stack_push(uk, STACK_TYPE_RECURSIVE, 1, Qnil);

VALUE obj;
if (num_fields == 0) {
/* Special case for empty structs */
obj = rb_class_new_instance(0, NULL, struct_class);
} else if (RTEST(keyword_init)) {
/* For keyword_init structs, build a hash with member names as keys */
VALUE kwargs = rb_hash_new();
for (long i = 0; i < num_fields; i++) {
int ret = msgpack_unpacker_read(uk, 0);
if (ret < 0) {
msgpack_unpacker_stack_pop(uk);
return ret;
}
VALUE key = rb_ary_entry(members, i);
rb_hash_aset(kwargs, key, uk->last_object);
}
/* Call new with keyword arguments */
obj = rb_class_new_instance_kw(1, &kwargs, struct_class, RB_PASS_KEYWORDS);
} else {
/* For regular structs, use positional arguments
* Use RB_ALLOCV to avoid stack overflow with large structs */
VALUE allocv_holder;
VALUE *values = RB_ALLOCV_N(VALUE, allocv_holder, num_fields);
for (int i = 0; i < num_fields; i++) {
int ret = msgpack_unpacker_read(uk, 0);
if (ret < 0) {
msgpack_unpacker_stack_pop(uk);
RB_ALLOCV_END(allocv_holder);
return ret;
}
values[i] = uk->last_object;
VALUE obj = rb_obj_alloc(struct_class);
long num_fields = RSTRUCT_LEN(obj);

for (int i = 0; i < num_fields; i++) {
int ret = msgpack_unpacker_read(uk, 0);
if (RB_UNLIKELY(ret < 0)) {
msgpack_unpacker_stack_pop(uk);
return ret;
}
obj = rb_class_new_instance((int)num_fields, values, struct_class);
RB_ALLOCV_END(allocv_holder);
RSTRUCT_SET(obj, i, uk->last_object);
}

RB_GC_GUARD(struct_class);
RB_GC_GUARD(members);
msgpack_unpacker_stack_pop(uk);
return object_complete(uk, obj);
}
Expand Down