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

ruby-changes:62264

From: Kenta <ko1@a...>
Date: Sat, 18 Jul 2020 23:45:19 +0900 (JST)
Subject: [ruby-changes:62264] a63f520971 (master): Optimize Array#max (#3325)

https://git.ruby-lang.org/ruby.git/commit/?id=a63f520971

From a63f520971787aa7b32b27486e9a5bb732d2814e Mon Sep 17 00:00:00 2001
From: Kenta Murata <mrkn@u...>
Date: Sat, 18 Jul 2020 23:45:00 +0900
Subject: Optimize Array#max (#3325)

The benchmark result is below:

|                |compare-ruby|built-ruby|
|:---------------|-----------:|---------:|
|ary2.max        |     38.837M|   40.830M|
|                |           -|     1.05x|
|ary10.max       |     23.035M|   32.626M|
|                |           -|     1.42x|
|ary100.max      |      5.490M|   11.020M|
|                |           -|     2.01x|
|ary500.max      |      1.324M|    2.679M|
|                |           -|     2.02x|
|ary1000.max     |    699.167k|    1.403M|
|                |           -|     2.01x|
|ary2000.max     |    284.321k|  570.446k|
|                |           -|     2.01x|
|ary3000.max     |    282.613k|  571.683k|
|                |           -|     2.02x|
|ary5000.max     |    145.120k|  285.546k|
|                |           -|     1.97x|
|ary10000.max    |     72.102k|  142.831k|
|                |           -|     1.98x|
|ary20000.max    |     36.065k|   72.077k|
|                |           -|     2.00x|
|ary50000.max    |     14.343k|   29.139k|
|                |           -|     2.03x|
|ary100000.max   |      7.586k|   14.472k|
|                |           -|     1.91x|
|ary1000000.max  |     726.915|    1.495k|
|                |           -|     2.06x|

diff --git a/array.c b/array.c
index 7d1fdf2..5fed5e5 100644
--- a/array.c
+++ b/array.c
@@ -6177,6 +6177,95 @@ rb_ary_union_multi(int argc, VALUE *argv, VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L6177
     return ary_union;
 }
 
+static VALUE
+ary_max_generic(VALUE ary, long i, VALUE vmax)
+{
+    RUBY_ASSERT(i > 0 && i < RARRAY_LEN(ary));
+
+    VALUE v;
+    for (; i < RARRAY_LEN(ary); ++i) {
+        v = RARRAY_AREF(ary, i);
+
+        if (rb_cmpint(rb_funcallv(vmax, id_cmp, 1, &v), vmax, v) < 0) {
+            vmax = v;
+        }
+    }
+
+    return vmax;
+}
+
+static VALUE
+ary_max_opt_fixnum(VALUE ary, long i, VALUE vmax)
+{
+    const long n = RARRAY_LEN(ary);
+    RUBY_ASSERT(i > 0 && i < n);
+    RUBY_ASSERT(FIXNUM_P(vmax));
+
+    VALUE v;
+    for (; i < n; ++i) {
+        v = RARRAY_AREF(ary, i);
+
+        if (FIXNUM_P(v)) {
+            if ((long)vmax < (long)v) {
+                vmax = v;
+            }
+        }
+        else {
+            return ary_max_generic(ary, i, vmax);
+        }
+    }
+
+    return vmax;
+}
+
+static VALUE
+ary_max_opt_float(VALUE ary, long i, VALUE vmax)
+{
+    const long n = RARRAY_LEN(ary);
+    RUBY_ASSERT(i > 0 && i < n);
+    RUBY_ASSERT(RB_FLOAT_TYPE_P(vmax));
+
+    VALUE v;
+    for (; i < n; ++i) {
+        v = RARRAY_AREF(ary, i);
+
+        if (RB_FLOAT_TYPE_P(v)) {
+            if (rb_float_cmp(vmax, v) < 0) {
+                vmax = v;
+            }
+        }
+        else {
+            return ary_max_generic(ary, i, vmax);
+        }
+    }
+
+    return vmax;
+}
+
+static VALUE
+ary_max_opt_string(VALUE ary, long i, VALUE vmax)
+{
+    const long n = RARRAY_LEN(ary);
+    RUBY_ASSERT(i > 0 && i < n);
+    RUBY_ASSERT(STRING_P(vmax));
+
+    VALUE v;
+    for (; i < n; ++i) {
+        v = RARRAY_AREF(ary, i);
+
+        if (STRING_P(v)) {
+            if (rb_str_cmp(vmax, v) < 0) {
+                vmax = v;
+            }
+        }
+        else {
+            return ary_max_generic(ary, i, vmax);
+        }
+    }
+
+    return vmax;
+}
+
 /*
  *  call-seq:
  *     ary.max                     -> obj
@@ -6210,6 +6299,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L6299
     if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
        return rb_nmin_run(ary, num, 0, 1, 1);
 
+    const long n = RARRAY_LEN(ary);
     if (rb_block_given_p()) {
 	for (i = 0; i < RARRAY_LEN(ary); i++) {
 	   v = RARRAY_AREF(ary, i);
@@ -6218,13 +6308,22 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L6308
 	   }
 	}
     }
-    else {
-	for (i = 0; i < RARRAY_LEN(ary); i++) {
-	   v = RARRAY_AREF(ary, i);
-	   if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
-	       result = v;
-	   }
-	}
+    else if (n > 0) {
+        result = RARRAY_AREF(ary, 0);
+        if (n > 1) {
+            if (FIXNUM_P(result) && CMP_OPTIMIZABLE(cmp_opt, Integer)) {
+                return ary_max_opt_fixnum(ary, 1, result);
+            }
+            else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
+                return ary_max_opt_string(ary, 1, result);
+            }
+            else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
+                return ary_max_opt_float(ary, 1, result);
+            }
+            else {
+                return ary_max_generic(ary, 1, result);
+            }
+        }
     }
     if (result == Qundef) return Qnil;
     return result;
diff --git a/benchmark/array_max_float.yml b/benchmark/array_max_float.yml
new file mode 100644
index 0000000..ace1ae2
--- /dev/null
+++ b/benchmark/array_max_float.yml
@@ -0,0 +1,30 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/array_max_float.yml#L1
+prelude: |
+  ary2 = 2.times.map(&:to_f).shuffle
+  ary10 = 10.times.map(&:to_f).shuffle
+  ary100 = 100.times.map(&:to_f).shuffle
+  ary500 = 500.times.map(&:to_f).shuffle
+  ary1000 = 1000.times.map(&:to_f).shuffle
+  ary2000 = 2500.times.map(&:to_f).shuffle
+  ary3000 = 2500.times.map(&:to_f).shuffle
+  ary5000 = 5000.times.map(&:to_f).shuffle
+  ary10000 = 10000.times.map(&:to_f).shuffle
+  ary20000 = 20000.times.map(&:to_f).shuffle
+  ary50000 = 50000.times.map(&:to_f).shuffle
+  ary100000 = 100000.times.map(&:to_f).shuffle
+
+benchmark:
+  ary2.max: ary2.max
+  ary10.max: ary10.max
+  ary100.max: ary100.max
+  ary500.max: ary500.max
+  ary1000.max: ary1000.max
+  ary2000.max: ary2000.max
+  ary3000.max: ary3000.max
+  ary5000.max: ary5000.max
+  ary10000.max: ary10000.max
+  ary20000.max: ary20000.max
+  ary50000.max: ary50000.max
+  ary100000.max: ary100000.max
+
+loop_count: 10000
+
diff --git a/benchmark/array_max_int.yml b/benchmark/array_max_int.yml
new file mode 100644
index 0000000..acd8368
--- /dev/null
+++ b/benchmark/array_max_int.yml
@@ -0,0 +1,31 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/array_max_int.yml#L1
+prelude: |
+  ary2 = 2.times.to_a.shuffle
+  ary10 = 10.times.to_a.shuffle
+  ary100 = 100.times.to_a.shuffle
+  ary500 = 500.times.to_a.shuffle
+  ary1000 = 1000.times.to_a.shuffle
+  ary2000 = 2500.times.to_a.shuffle
+  ary3000 = 2500.times.to_a.shuffle
+  ary5000 = 5000.times.to_a.shuffle
+  ary10000 = 10000.times.to_a.shuffle
+  ary20000 = 20000.times.to_a.shuffle
+  ary50000 = 50000.times.to_a.shuffle
+  ary100000 = 100000.times.to_a.shuffle
+  ary1000000 = 1000000.times.to_a.shuffle
+
+benchmark:
+  ary2.max: ary2.max
+  ary10.max: ary10.max
+  ary100.max: ary100.max
+  ary500.max: ary500.max
+  ary1000.max: ary1000.max
+  ary2000.max: ary2000.max
+  ary3000.max: ary3000.max
+  ary5000.max: ary5000.max
+  ary10000.max: ary10000.max
+  ary20000.max: ary20000.max
+  ary50000.max: ary50000.max
+  ary100000.max: ary100000.max
+  ary1000000.max: ary1000000.max
+
+loop_count: 10000
diff --git a/benchmark/array_max_str.yml b/benchmark/array_max_str.yml
new file mode 100644
index 0000000..2aeed01
--- /dev/null
+++ b/benchmark/array_max_str.yml
@@ -0,0 +1,30 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/array_max_str.yml#L1
+prelude: |
+  ary2 = 2.times.map(&:to_s).shuffle
+  ary10 = 10.times.map(&:to_s).shuffle
+  ary100 = 100.times.map(&:to_s).shuffle
+  ary500 = 500.times.map(&:to_s).shuffle
+  ary1000 = 1000.times.map(&:to_s).shuffle
+  ary2000 = 2500.times.map(&:to_s).shuffle
+  ary3000 = 2500.times.map(&:to_s).shuffle
+  ary5000 = 5000.times.map(&:to_s).shuffle
+  ary10000 = 10000.times.map(&:to_s).shuffle
+  ary20000 = 20000.times.map(&:to_s).shuffle
+  ary50000 = 50000.times.map(&:to_s).shuffle
+  ary100000 = 100000.times.map(&:to_s).shuffle
+
+benchmark:
+  ary2.max: ary2.max
+  ary10.max: ary10.max
+  ary100.max: ary100.max
+  ary500.max: ary500.max
+  ary1000.max: ary1000.max
+  ary2000.max: ary2000.max
+  ary3000.max: ary3000.max
+  ary5000.max: ary5000.max
+  ary10000.max: ary10000.max
+  ary20000.max: ary20000.max
+  ary50000.max: ary50000.max
+  ary100000.max: ary100000.max
+
+loop_count: 10000
+
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index a66d230..2b48dcf 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -1756,10 +1756,12 @@ class TestArray < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_array.rb#L1756
   end
 
   def test_max
+    assert_equal(1, [1].max)
     assert_equal(3, [1, 2, 3, 1, 2].max)
     assert_equal(1, [1, 2, 3, 1, 2].max {|a,b| b <=> a })
     cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
     assert_equal([1, 3], [1, 2, 3, 1, 2].each_with_index.max(&cond))
+    assert_equal(3.0, [1.0, 3.0, 2.0].max)
     ary = %w(albatross dog horse)
     assert_equal("horse", ary.max)
     assert_equal("albatross", ary.max {|a,b| a.length <=> b.length })
@@ -1777,6 +1779,7 @@ class TestArray < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_array.rb#L1779
   end
 
   def test_minmax
+    assert_equal([3, 3], [3].minmax)
     assert_equal([1, 3], [1, 2, 3, 1, 2].minmax)
     assert_equal([3, 1], [1, 2, 3, 1, 2].minmax {|a,b| b <=> a })
     cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
-- 
cgit v0.10.2


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

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