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

ruby-changes:46853

From: watson1978 <ko1@a...>
Date: Tue, 30 May 2017 18:01:02 +0900 (JST)
Subject: [ruby-changes:46853] watson1978:r58968 (trunk): Improve performance of Enumerable#{sort_by, min_by, max_by, minmax_by}

watson1978	2017-05-30 18:00:56 +0900 (Tue, 30 May 2017)

  New Revision: 58968

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

  Log:
    Improve performance of Enumerable#{sort_by,min_by,max_by,minmax_by}
    
    This is totally same approach with r58964.
    
    enum.c (sort_by_cmp): use OPTIMIZED_CMP() to compare the objects instead of
        `<=>' method dispatching for Fixnum/Float/String object.
    
    enum.c (nmin_cmp): ditto.
    enum.c (min_by_i): ditto.
    enum.c (max_by_i): ditto.
    enum.c (minmax_by_i_update): ditto.
    enum.c (minmax_by_i): ditto.
    
        Enumerable#sort_by   -> 51 % up
        Enumerable#min_by(n) -> 34 % up
        Enumerable#min_by    -> 37 % up
        Enumerable#max_by(n) -> 61 % up
        Enumerable#max_by    -> 40 % up
        Enumerable#minmax_by -> 67 % up
    
        [ruby-core:80689] [Bug #13437] [Fix GH-1584]
    
    ### Before
      Enumerable#sort_by      5.692k (?\194?\177 2.2%) i/s -     28.611k in   5.028861s
    Enumerable#min_by(n)      8.496k (?\194?\177 0.5%) i/s -     43.146k in   5.078394s
       Enumerable#min_by      8.678k (?\194?\177 0.5%) i/s -     43.911k in   5.060128s
    Enumerable#max_by(n)      3.306k (?\194?\177 3.0%) i/s -     16.562k in   5.014727s
       Enumerable#max_by      8.322k (?\194?\177 2.8%) i/s -     42.400k in   5.099400s
    Enumerable#minmax_by      6.769k (?\194?\177 2.6%) i/s -     34.100k in   5.041354s
    
    ### After
      Enumerable#sort_by      8.591k (?\194?\177 3.0%) i/s -     43.316k in   5.046836s
    Enumerable#min_by(n)     11.489k (?\194?\177 1.2%) i/s -     57.732k in   5.025504s
       Enumerable#min_by     11.835k (?\194?\177 2.7%) i/s -     60.150k in   5.086450s
    Enumerable#max_by(n)      5.322k (?\194?\177 1.1%) i/s -     26.650k in   5.008289s
       Enumerable#max_by     11.705k (?\194?\177 0.6%) i/s -     59.262k in   5.062997s
    Enumerable#minmax_by     11.323k (?\194?\177 1.3%) i/s -     57.018k in   5.036565s
    
    ### Test code
    require 'benchmark/ips'
    
    Benchmark.ips do |x|
      enum = (1..1000).to_a.to_enum
    
      x.report "Enumerable#sort_by" do
        enum.sort_by { |a| a }
      end
    
      x.report "Enumerable#min_by(n)" do
        enum.min_by(2) { |a| a }
      end
    
      x.report "Enumerable#min_by" do
        enum.min_by { |a| a }
      end
    
      x.report "Enumerable#max_by(n)" do
        enum.max_by(2) { |a| a }
      end
    
      x.report "Enumerable#max_by" do
        enum.max_by { |a| a }
      end
    
      x.report "Enumerable#minmax_by" do
        enum.minmax_by { |a| a }
      end
    end

  Modified files:
    trunk/enum.c
Index: enum.c
===================================================================
--- enum.c	(revision 58967)
+++ enum.c	(revision 58968)
@@ -1010,6 +1010,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, https://github.com/ruby/ruby/blob/trunk/enum.c#L1010
 static int
 sort_by_cmp(const void *ap, const void *bp, void *data)
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
     VALUE a;
     VALUE b;
     VALUE ary = (VALUE)data;
@@ -1021,7 +1022,7 @@ sort_by_cmp(const void *ap, const void * https://github.com/ruby/ruby/blob/trunk/enum.c#L1022
     a = *(VALUE *)ap;
     b = *(VALUE *)bp;
 
-    return rb_cmpint(rb_funcall(a, id_cmp, 1, b), a, b);
+    return OPTIMIZED_CMP(a, b, cmp_opt);
 }
 
 /*
@@ -1267,13 +1268,13 @@ struct nmin_data { https://github.com/ruby/ruby/blob/trunk/enum.c#L1268
 static int
 nmin_cmp(const void *ap, const void *bp, void *_data)
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
     struct nmin_data *data = (struct nmin_data *)_data;
     VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
-    VALUE cmp = rb_funcall(a, id_cmp, 1, b);
     if (RBASIC(data->buf)->klass) {
 	rb_raise(rb_eRuntimeError, "%s reentered", data->method);
     }
-    return rb_cmpint(cmp, a, b);
+    return OPTIMIZED_CMP(a, b, cmp_opt);
 }
 
 static int
@@ -1872,6 +1873,7 @@ enum_minmax(VALUE obj) https://github.com/ruby/ruby/blob/trunk/enum.c#L1873
 static VALUE
 min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
     struct MEMO *memo = MEMO_CAST(args);
     VALUE v;
 
@@ -1882,7 +1884,7 @@ min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, a https://github.com/ruby/ruby/blob/trunk/enum.c#L1884
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
     }
-    else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->v1), v, memo->v1) < 0) {
+    else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) < 0) {
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
     }
@@ -1933,6 +1935,7 @@ enum_min_by(int argc, VALUE *argv, VALUE https://github.com/ruby/ruby/blob/trunk/enum.c#L1935
 static VALUE
 max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
     struct MEMO *memo = MEMO_CAST(args);
     VALUE v;
 
@@ -1943,7 +1946,7 @@ max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, a https://github.com/ruby/ruby/blob/trunk/enum.c#L1946
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
     }
-    else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->v1), v, memo->v1) > 0) {
+    else if (OPTIMIZED_CMP(v, memo->v1, cmp_opt) > 0) {
 	MEMO_V1_SET(memo, v);
 	MEMO_V2_SET(memo, i);
     }
@@ -2048,6 +2051,8 @@ struct minmax_by_t { https://github.com/ruby/ruby/blob/trunk/enum.c#L2051
 static void
 minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *memo)
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
+
     if (memo->min_bv == Qundef) {
 	memo->min_bv = v1;
 	memo->max_bv = v2;
@@ -2055,11 +2060,11 @@ minmax_by_i_update(VALUE v1, VALUE v2, V https://github.com/ruby/ruby/blob/trunk/enum.c#L2060
 	memo->max = i2;
     }
     else {
-	if (rb_cmpint(rb_funcall(v1, id_cmp, 1, memo->min_bv), v1, memo->min_bv) < 0) {
+	if (OPTIMIZED_CMP(v1, memo->min_bv, cmp_opt) < 0) {
 	    memo->min_bv = v1;
 	    memo->min = i1;
 	}
-	if (rb_cmpint(rb_funcall(v2, id_cmp, 1, memo->max_bv), v2, memo->max_bv) > 0) {
+	if (OPTIMIZED_CMP(v2, memo->max_bv, cmp_opt) > 0) {
 	    memo->max_bv = v2;
 	    memo->max = i2;
 	}
@@ -2069,6 +2074,7 @@ minmax_by_i_update(VALUE v1, VALUE v2, V https://github.com/ruby/ruby/blob/trunk/enum.c#L2074
 static VALUE
 minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
 {
+    struct cmp_opt_data cmp_opt = { 0, 0 };
     struct minmax_by_t *memo = MEMO_FOR(struct minmax_by_t, _memo);
     VALUE vi, vj, j;
     int n;
@@ -2086,7 +2092,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i https://github.com/ruby/ruby/blob/trunk/enum.c#L2092
     j = memo->last;
     memo->last_bv = Qundef;
 
-    n = rb_cmpint(rb_funcall(vj, id_cmp, 1, vi), vj, vi);
+    n = OPTIMIZED_CMP(vj, vi, cmp_opt);
     if (n == 0) {
         i = j;
         vi = vj;

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

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