[前][次][番号順一覧][スレッド一覧]

ruby-changes:46313

From: shyouhei <ko1@a...>
Date: Fri, 21 Apr 2017 13:21:18 +0900 (JST)
Subject: [ruby-changes:46313] shyouhei:r58427 (trunk): refactor hash literal

shyouhei	2017-04-21 13:21:14 +0900 (Fri, 21 Apr 2017)

  New Revision: 58427

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=58427

  Log:
    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

  Modified files:
    trunk/hash.c
    trunk/insns.def
    trunk/internal.h
    trunk/vm.c
Index: vm.c
===================================================================
--- vm.c	(revision 58426)
+++ vm.c	(revision 58427)
@@ -2646,7 +2646,6 @@ m_core_set_postexe(VALUE self) https://github.com/ruby/ruby/blob/trunk/vm.c#L2646
     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, c https://github.com/ruby/ruby/blob/trunk/vm.c#L2655
     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 a https://github.com/ruby/ruby/blob/trunk/vm.c#L2672
 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
Index: insns.def
===================================================================
--- insns.def	(revision 58426)
+++ insns.def	(revision 58427)
@@ -492,16 +492,13 @@ newhash https://github.com/ruby/ruby/blob/trunk/insns.def#L492
 (...)
 (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);
 }
Index: hash.c
===================================================================
--- hash.c	(revision 58426)
+++ hash.c	(revision 58427)
@@ -626,6 +626,103 @@ rb_hash_initialize(int argc, VALUE *argv https://github.com/ruby/ruby/blob/trunk/hash.c#L626
     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 https://github.com/ruby/ruby/blob/trunk/hash.c#L746
 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
Index: internal.h
===================================================================
--- internal.h	(revision 58426)
+++ internal.h	(revision 58427)
@@ -1236,6 +1236,7 @@ VALUE rb_hash_rehash(VALUE hash); https://github.com/ruby/ruby/blob/trunk/internal.h#L1236
 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);

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]