diff --git a/array.c b/array.c index 7d1fdf2e34..5fed5e5788 100644 --- a/array.c +++ b/array.c @@ -6177,6 +6177,95 @@ rb_ary_union_multi(int argc, VALUE *argv, VALUE ary) 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) 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) } } } - 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 0000000000..ace1ae2e14 --- /dev/null +++ b/benchmark/array_max_float.yml @@ -0,0 +1,30 @@ +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 0000000000..acd83684d0 --- /dev/null +++ b/benchmark/array_max_int.yml @@ -0,0 +1,31 @@ +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 0000000000..2aeed010f2 --- /dev/null +++ b/benchmark/array_max_str.yml @@ -0,0 +1,30 @@ +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 a66d2301d0..2b48dcf308 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1756,10 +1756,12 @@ class TestArray < Test::Unit::TestCase 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 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 }