Last active
August 29, 2015 14:20
-
-
Save skippy/63c9025db92019886aa0 to your computer and use it in GitHub Desktop.
hash optimizations (c vs mix ruby/c vs jruby)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require 'rubygems' | |
require 'bundler/setup' | |
require 'benchmark' | |
require 'google/protobuf' | |
pool = Google::Protobuf::DescriptorPool.new | |
pool.build do | |
add_message "TestMessage" do | |
optional :optional_int32, :int32, 1 | |
optional :optional_int64, :int64, 2 | |
optional :optional_uint32, :uint32, 3 | |
optional :optional_uint64, :uint64, 4 | |
optional :optional_bool, :bool, 5 | |
optional :optional_float, :float, 6 | |
optional :optional_double, :double, 7 | |
optional :optional_string, :string, 8 | |
optional :optional_bytes, :bytes, 9 | |
optional :optional_msg, :message, 10, "TestMessage2" | |
repeated :repeated_int32, :int32, 12 | |
repeated :repeated_int64, :int64, 13 | |
repeated :repeated_uint32, :uint32, 14 | |
repeated :repeated_uint64, :uint64, 15 | |
repeated :repeated_bool, :bool, 16 | |
repeated :repeated_float, :float, 17 | |
repeated :repeated_double, :double, 18 | |
repeated :repeated_string, :string, 19 | |
repeated :repeated_bytes, :bytes, 20 | |
repeated :repeated_msg, :message, 21, "TestMessage2" | |
end | |
add_message "TestMessage2" do | |
optional :foo, :int32, 1 | |
end | |
end | |
TestMessage = pool.lookup("TestMessage").msgclass | |
TestMessage2 = pool.lookup("TestMessage2").msgclass | |
tm = TestMessage.new | |
tm.repeated_string << 'ok1' | |
tm.repeated_string << 'ok2' | |
tm.optional_string = 'another string' | |
tm.optional_int32 = 102 | |
iters = 100_000 | |
Benchmark.bm(12) do |b| | |
b.report('#to_h-java'){ iters.times{ tm.to_h } } | |
b.report('#to_h-pure'){ iters.times{ tm.to_h2 } } | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// just the relavent method | |
VALUE Message_to_h(VALUE _self) { | |
MessageHeader* self; | |
TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); | |
VALUE hash = rb_hash_new(); | |
rb_gc_register_address(&hash); | |
upb_msg_field_iter it; | |
for (upb_msg_field_begin(&it, self->descriptor->msgdef); | |
!upb_msg_field_done(&it); | |
upb_msg_field_next(&it)) { | |
const upb_fielddef* field = upb_msg_iter_field(&it); | |
VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self), field); | |
// see benchmark results for the difference between using a symbol and a string | |
// VALUE msg_key = rb_str_new2(upb_fielddef_name(field)); | |
VALUE msg_key = ID2SYM(upb_fielddef_name(field)); | |
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { | |
msg_value = RepeatedField_to_ary(msg_value); | |
} | |
rb_hash_aset(hash, msg_key, msg_value); | |
} | |
return hash; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// just the relavent method | |
@JRubyMethod(name = {"to_h", "to_hash"}) | |
public IRubyObject toHash(ThreadContext context) { | |
Ruby runtime = context.runtime; | |
RubyHash ret = RubyHash.newHash(runtime); | |
for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) { | |
IRubyObject value = getField(context, fdef); | |
if (value.respondsTo("to_h")) { | |
value = Helpers.invoke(context, value, "to_h"); | |
} else if (value.respondsTo("to_a")) { | |
value = Helpers.invoke(context, value, "to_a"); | |
} | |
ret.fastASet(runtime.newString(fdef.getName()), value); | |
} | |
return ret; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# just the ruby implementation | |
# which is backed by self.class.descriptor.each, which is a custom c method | |
def to_h2 | |
self.class.descriptor.each_with_object({}) do |field, hash| | |
name = field.name | |
val = self[name] | |
val = val.to_a if val.respond_to?(:to_a) | |
hash[name] = val | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### MRI 2.2.0 | |
#using strings as keys | |
user system total real | |
#to_h-c 1.460000 0.010000 1.470000 ( 1.491718) | |
#to_h-pure 3.080000 0.030000 3.110000 ( 3.179323) | |
#using symbols as keys | |
user system total real | |
#to_h-c 0.350000 0.010000 0.360000 ( 0.371077) | |
#to_h-pure 2.750000 0.050000 2.800000 ( 2.881573) | |
### jruby-1.7.19 | |
# NOTE: I know jruby has some benchmarking things to watch out for... here I just fired up pry and ran the script... not ideal | |
# but it does seem that some optimisations may be needed | |
#using strings as keys | |
user system total real | |
#to_h-java 2.350000 0.020000 2.370000 ( 2.392000) | |
#to_h-pure 5.710000 0.080000 5.790000 ( 6.324000) | |
#using symbols as keys | |
user system total real | |
#to_h-java 0.980000 0.020000 1.000000 ( 0.991000) | |
#to_h-pure 5.720000 0.110000 5.830000 ( 5.900000) | |
### jruby 9.0.0.0.pre1 and jruby 9.0.0.0.pre2 | |
fails with: | |
TypeError: wrong element type String (expected array) | |
from org/jruby/RubyArray.java:1569:in `each' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment