mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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
This commit is contained in:
parent
bdc6d41640
commit
4ee09d914d
4 changed files with 117 additions and 91 deletions
121
hash.c
121
hash.c
|
@ -626,48 +626,69 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
|
|||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Hash[ key, value, ... ] -> new_hash
|
||||
* Hash[ [ [key, value], ... ] ] -> new_hash
|
||||
* Hash[ object ] -> new_hash
|
||||
*
|
||||
* Creates a new hash populated with the given objects.
|
||||
*
|
||||
* Similar to the literal <code>{ _key_ => _value_, ... }</code>. In the first
|
||||
* form, keys and values occur in pairs, so there must be an even number of
|
||||
* arguments.
|
||||
*
|
||||
* The second and third form take a single argument which is either an array
|
||||
* of key-value pairs or an object convertible to a hash.
|
||||
*
|
||||
* Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
|
||||
* Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
|
||||
* Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
|
||||
*/
|
||||
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_s_create(int argc, VALUE *argv, VALUE klass)
|
||||
rb_hash_new_from_values_with_klass(long argc, const VALUE *argv, VALUE klass)
|
||||
{
|
||||
VALUE hash, tmp;
|
||||
int i;
|
||||
long i;
|
||||
st_table *t;
|
||||
|
||||
if (argc == 1) {
|
||||
tmp = rb_hash_s_try_convert(Qnil, argv[0]);
|
||||
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)) {
|
||||
hash = hash_alloc(klass);
|
||||
VALUE 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]);
|
||||
tmp = rb_check_array_type(obj);
|
||||
if (!NIL_P(tmp)) {
|
||||
long i;
|
||||
|
||||
hash = hash_alloc(klass);
|
||||
for (i = 0; i < RARRAY_LEN(tmp); ++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;
|
||||
|
@ -693,25 +714,43 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
|
|||
val = RARRAY_AREF(v, 1);
|
||||
case 1:
|
||||
key = RARRAY_AREF(v, 0);
|
||||
rb_hash_aset(hash, key, val);
|
||||
hash_insert_raw(tbl, key, val);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
return hash_alloc_from_st(klass, tbl);
|
||||
}
|
||||
}
|
||||
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]);
|
||||
}
|
||||
/*
|
||||
* call-seq:
|
||||
* Hash[ key, value, ... ] -> new_hash
|
||||
* Hash[ [ [key, value], ... ] ] -> new_hash
|
||||
* Hash[ object ] -> new_hash
|
||||
*
|
||||
* Creates a new hash populated with the given objects.
|
||||
*
|
||||
* Similar to the literal <code>{ _key_ => _value_, ... }</code>. In the first
|
||||
* form, keys and values occur in pairs, so there must be an even number of
|
||||
* arguments.
|
||||
*
|
||||
* The second and third form take a single argument which is either an array
|
||||
* of key-value pairs or an object convertible to a hash.
|
||||
*
|
||||
* Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
|
||||
* Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
|
||||
* Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
|
||||
*/
|
||||
|
||||
return hash;
|
||||
static VALUE
|
||||
rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
|
11
insns.def
11
insns.def
|
@ -492,16 +492,13 @@ newhash
|
|||
(...)
|
||||
(VALUE val) // inc += 1 - num;
|
||||
{
|
||||
rb_num_t i;
|
||||
|
||||
RUBY_DTRACE_CREATE_HOOK(HASH, num);
|
||||
|
||||
if (num) {
|
||||
val = rb_hash_new_from_values(num, STACK_ADDR_FROM_TOP(num));
|
||||
}
|
||||
else {
|
||||
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);
|
||||
}
|
||||
POPN(num);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
31
vm.c
31
vm.c
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue