diff options
author | shyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-04-21 04:21:14 +0000 |
---|---|---|
committer | shyouhei <shyouhei@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-04-21 04:21:14 +0000 |
commit | 36682d0e1ce3ef92ef1801e0f6a455e4ebe7ab17 (patch) | |
tree | c66b92a237f70abb15d61124f4c685848fba6f72 | |
parent | b811b97458bd92de58d97b9ba400ae076c7c5967 (diff) | |
download | ruby-36682d0e1ce3ef92ef1801e0f6a455e4ebe7ab17.tar.gz |
refactor hash literal
Same as rb_ary_tmp_new_from_values(), it reduces vm_exec_core binary
size from 26,176 bytes to 26,080 bytes. But this time, also with a
bit of optimizations:
- Because we are allocating a new hash and no back references are
introduced at all, we can safely skip write barriers.
- Also, the iteration never recurs. We can avoid complicated
function callbacks by using st_insert instead of st_update.
----
* hash.c (rb_hash_new_from_values): refactor
extract the bulk insert into a function.
* hash.c (rb_hash_new_from_object): also refactor.
* hash.c (rb_hash_s_create): use the new functions.
* insns.def (newhash): ditto.
* vm.c (core_hash_from_ary): ditto.
* iternal.h: export the new function.
-----------------------------------------------------------
benchmark results:
minimum results in each 7 measurements.
Execution time (sec)
name before after
loop_whileloop2 0.135 0.134
vm2_bighash* 1.236 0.687
Speedup ratio: compare with the result of `before' (greater is better)
name after
loop_whileloop2 1.008
vm2_bighash* 1.798
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58427 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | hash.c | 163 | ||||
-rw-r--r-- | insns.def | 13 | ||||
-rw-r--r-- | internal.h | 1 | ||||
-rw-r--r-- | vm.c | 31 |
4 files changed, 117 insertions, 91 deletions
@@ -626,6 +626,103 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash) return hash; } +static inline VALUE +hash_alloc_from_st(VALUE klass, st_table *ntbl) +{ + VALUE h = hash_alloc(klass); + RHASH(h)->ntbl = ntbl; + return h; +} + +static inline int +hash_insert_raw(st_table *tbl, VALUE key, VALUE val) +{ + st_data_t v = (st_data_t)val; + st_data_t k = (rb_obj_class(k) == rb_cString) ? + (st_data_t)rb_str_new_frozen(key) : + (st_data_t)key; + + return st_insert(tbl, k, v); +} + +static VALUE +rb_hash_new_from_values_with_klass(long argc, const VALUE *argv, VALUE klass) +{ + long i; + st_table *t; + + if (argc % 2) { + rb_raise(rb_eArgError, "odd number of arguments for Hash"); + } + + t = st_init_table_with_size(&objhash, argc / 2); + for (i = 0; i < argc; /* */) { + VALUE key = argv[i++]; + VALUE val = argv[i++]; + + hash_insert_raw(t, key, val); + } + return hash_alloc_from_st(klass, t); +} + +VALUE +rb_hash_new_from_values(long argc, const VALUE *argv) +{ + return rb_hash_new_from_values_with_klass(argc, argv, rb_cHash); +} + +static VALUE +rb_hash_new_from_object(VALUE klass, VALUE obj) +{ + VALUE tmp = rb_hash_s_try_convert(Qnil, obj); + if (!NIL_P(tmp)) { + VALUE hash = hash_alloc(klass); + if (RHASH(tmp)->ntbl) { + RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl); + } + return hash; + } + + tmp = rb_check_array_type(obj); + if (!NIL_P(tmp)) { + long i; + long len = RARRAY_LEN(tmp); + st_table *tbl = len ? st_init_table_with_size(&objhash, len) : NULL; + for (i = 0; i < len; ++i) { + VALUE e = RARRAY_AREF(tmp, i); + VALUE v = rb_check_array_type(e); + VALUE key, val = Qnil; + + if (NIL_P(v)) { +#if 0 /* refix in the next release */ + rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)", + rb_builtin_class_name(e), i); + +#else + rb_warn("wrong element type %s at %ld (expected array)", + rb_builtin_class_name(e), i); + rb_warn("ignoring wrong elements is deprecated, remove them explicitly"); + rb_warn("this causes ArgumentError in the next release"); + continue; +#endif + } + switch (RARRAY_LEN(v)) { + default: + rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)", + RARRAY_LEN(v)); + case 2: + val = RARRAY_AREF(v, 1); + case 1: + key = RARRAY_AREF(v, 0); + hash_insert_raw(tbl, key, val); + } + } + return hash_alloc_from_st(klass, tbl); + } + + rb_raise(rb_eArgError, "odd number of arguments for Hash"); +} + /* * call-seq: * Hash[ key, value, ... ] -> new_hash @@ -649,69 +746,11 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash) static VALUE rb_hash_s_create(int argc, VALUE *argv, VALUE klass) { - VALUE hash, tmp; - int i; - - if (argc == 1) { - tmp = rb_hash_s_try_convert(Qnil, argv[0]); - if (!NIL_P(tmp)) { - hash = hash_alloc(klass); - if (RHASH(tmp)->ntbl) { - RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl); - } - return hash; - } - - tmp = rb_check_array_type(argv[0]); - if (!NIL_P(tmp)) { - long i; - - hash = hash_alloc(klass); - for (i = 0; i < RARRAY_LEN(tmp); ++i) { - VALUE e = RARRAY_AREF(tmp, i); - VALUE v = rb_check_array_type(e); - VALUE key, val = Qnil; - - if (NIL_P(v)) { -#if 0 /* refix in the next release */ - rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)", - rb_builtin_class_name(e), i); - -#else - rb_warn("wrong element type %s at %ld (expected array)", - rb_builtin_class_name(e), i); - rb_warn("ignoring wrong elements is deprecated, remove them explicitly"); - rb_warn("this causes ArgumentError in the next release"); - continue; -#endif - } - switch (RARRAY_LEN(v)) { - default: - rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)", - RARRAY_LEN(v)); - case 2: - val = RARRAY_AREF(v, 1); - case 1: - key = RARRAY_AREF(v, 0); - rb_hash_aset(hash, key, val); - } - } - return hash; - } + switch (argc) { + case 0: return hash_alloc(klass); + case 1: return rb_hash_new_from_object(klass, argv[0]); + default: return rb_hash_new_from_values_with_klass(argc, argv, klass); } - if (argc % 2 != 0) { - rb_raise(rb_eArgError, "odd number of arguments for Hash"); - } - - hash = hash_alloc(klass); - if (argc > 0) { - RHASH(hash)->ntbl = st_init_table_with_size(&objhash, argc / 2); - } - for (i=0; i<argc; i+=2) { - rb_hash_aset(hash, argv[i], argv[i + 1]); - } - - return hash; } static VALUE @@ -492,16 +492,13 @@ newhash (...) (VALUE val) // inc += 1 - num; { - rb_num_t i; - RUBY_DTRACE_CREATE_HOOK(HASH, num); - val = rb_hash_new(); - - for (i = num; i > 0; i -= 2) { - const VALUE v = TOPN(i - 2); - const VALUE k = TOPN(i - 1); - rb_hash_aset(val, k, v); + if (num) { + val = rb_hash_new_from_values(num, STACK_ADDR_FROM_TOP(num)); + } + else { + val = rb_hash_new(); } POPN(num); } diff --git a/internal.h b/internal.h index afea318376..de89418856 100644 --- a/internal.h +++ b/internal.h @@ -1236,6 +1236,7 @@ VALUE rb_hash_rehash(VALUE hash); int rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val); #define HASH_DELETED FL_USER1 #define HASH_PROC_DEFAULT FL_USER2 +extern VALUE rb_hash_new_from_values(long, const VALUE *); /* inits.c */ void rb_call_inits(void); @@ -2646,7 +2646,6 @@ m_core_set_postexe(VALUE self) return Qnil; } -static VALUE core_hash_merge_ary(VALUE hash, VALUE ary); static VALUE core_hash_from_ary(VALUE ary); static VALUE core_hash_merge_kwd(int argc, VALUE *argv); @@ -2656,7 +2655,6 @@ core_hash_merge(VALUE hash, long argc, const VALUE *argv) long i; Check_Type(hash, T_HASH); - VM_ASSERT(argc % 2 == 0); for (i=0; i<argc; i+=2) { rb_hash_aset(hash, argv[i], argv[i+1]); } @@ -2674,27 +2672,18 @@ m_core_hash_from_ary(VALUE self, VALUE ary) static VALUE core_hash_from_ary(VALUE ary) { - VALUE hash = rb_hash_new(); + long n; - RUBY_DTRACE_CREATE_HOOK(HASH, (Check_Type(ary, T_ARRAY), RARRAY_LEN(ary))); - return core_hash_merge_ary(hash, ary); -} - -#if 0 -static VALUE -m_core_hash_merge_ary(VALUE self, VALUE hash, VALUE ary) -{ - REWIND_CFP(core_hash_merge_ary(hash, ary)); - return hash; -} -#endif - -static VALUE -core_hash_merge_ary(VALUE hash, VALUE ary) -{ Check_Type(ary, T_ARRAY); - core_hash_merge(hash, RARRAY_LEN(ary), RARRAY_CONST_PTR(ary)); - return hash; + n = RARRAY_LEN(ary); + RUBY_DTRACE_CREATE_HOOK(HASH, n); + if (n) { + VM_ASSERT(n % 2 == 0); + return rb_hash_new_from_values(n, RARRAY_PTR(ary)); + } + else { + return rb_hash_new(); + } } static VALUE |