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:
shyouhei 2017-04-21 04:21:14 +00:00
Родитель bdc6d41640
Коммит 4ee09d914d
4 изменённых файлов: 117 добавлений и 91 удалений

163
hash.c
Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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
Просмотреть файл

@ -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