1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Optimize Array#min (#3324)

The benchmark result is below:

|                |compare-ruby|built-ruby|
|:---------------|-----------:|---------:|
|ary2.min        |     39.105M|   39.442M|
|                |           -|     1.01x|
|ary10.min       |     23.995M|   30.762M|
|                |           -|     1.28x|
|ary100.min      |      6.249M|   10.783M|
|                |           -|     1.73x|
|ary500.min      |      1.408M|    2.714M|
|                |           -|     1.93x|
|ary1000.min     |    828.397k|    1.465M|
|                |           -|     1.77x|
|ary2000.min     |    332.256k|  570.504k|
|                |           -|     1.72x|
|ary3000.min     |    338.079k|  573.868k|
|                |           -|     1.70x|
|ary5000.min     |    168.217k|  286.114k|
|                |           -|     1.70x|
|ary10000.min    |     85.512k|  143.551k|
|                |           -|     1.68x|
|ary20000.min    |     43.264k|   71.935k|
|                |           -|     1.66x|
|ary50000.min    |     17.317k|   29.107k|
|                |           -|     1.68x|
|ary100000.min   |      9.072k|   14.540k|
|                |           -|     1.60x|
|ary1000000.min  |     872.930|    1.436k|
|                |           -|     1.64x|

compare-ruby is 9f4b7fc82e.
This commit is contained in:
Kenta Murata 2020-07-18 23:45:25 +09:00 committed by GitHub
parent a63f520971
commit b4e784434c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
Notes: git 2020-07-18 23:45:48 +09:00
Merged-By: mrkn <mrkn@ruby-lang.org>
3 changed files with 139 additions and 7 deletions

113
array.c
View file

@ -6329,6 +6329,95 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary)
return result;
}
static VALUE
ary_min_generic(VALUE ary, long i, VALUE vmin)
{
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(vmin, id_cmp, 1, &v), vmin, v) > 0) {
vmin = v;
}
}
return vmin;
}
static VALUE
ary_min_opt_fixnum(VALUE ary, long i, VALUE vmin)
{
const long n = RARRAY_LEN(ary);
RUBY_ASSERT(i > 0 && i < n);
RUBY_ASSERT(FIXNUM_P(vmin));
VALUE a;
for (; i < n; ++i) {
a = RARRAY_AREF(ary, i);
if (FIXNUM_P(a)) {
if ((long)vmin > (long)a) {
vmin = a;
}
}
else {
return ary_min_generic(ary, i, vmin);
}
}
return vmin;
}
static VALUE
ary_min_opt_float(VALUE ary, long i, VALUE vmin)
{
const long n = RARRAY_LEN(ary);
RUBY_ASSERT(i > 0 && i < n);
RUBY_ASSERT(RB_FLOAT_TYPE_P(vmin));
VALUE a;
for (; i < n; ++i) {
a = RARRAY_AREF(ary, i);
if (RB_FLOAT_TYPE_P(a)) {
if (rb_float_cmp(vmin, a) > 0) {
vmin = a;
}
}
else {
return ary_min_generic(ary, i, vmin);
}
}
return vmin;
}
static VALUE
ary_min_opt_string(VALUE ary, long i, VALUE vmin)
{
const long n = RARRAY_LEN(ary);
RUBY_ASSERT(i > 0 && i < n);
RUBY_ASSERT(STRING_P(vmin));
VALUE a;
for (; i < n; ++i) {
a = RARRAY_AREF(ary, i);
if (STRING_P(a)) {
if (rb_str_cmp(vmin, a) > 0) {
vmin = a;
}
}
else {
return ary_min_generic(ary, i, vmin);
}
}
return vmin;
}
/*
* call-seq:
* ary.min -> obj
@ -6362,6 +6451,7 @@ rb_ary_min(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, 0, 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);
@ -6370,13 +6460,22 @@ rb_ary_min(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_min_opt_fixnum(ary, 1, result);
}
else if (STRING_P(result) && CMP_OPTIMIZABLE(cmp_opt, String)) {
return ary_min_opt_string(ary, 1, result);
}
else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(cmp_opt, Float)) {
return ary_min_opt_float(ary, 1, result);
}
else {
return ary_min_generic(ary, 1, result);
}
}
}
if (result == Qundef) return Qnil;
return result;

31
benchmark/array_min.yml Normal file
View file

@ -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.min: ary2.min
ary10.min: ary10.min
ary100.min: ary100.min
ary500.min: ary500.min
ary1000.min: ary1000.min
ary2000.min: ary2000.min
ary3000.min: ary3000.min
ary5000.min: ary5000.min
ary10000.min: ary10000.min
ary20000.min: ary20000.min
ary50000.min: ary50000.min
ary100000.min: ary100000.min
ary1000000.min: ary1000000.min
loop_count: 10000

View file

@ -1734,10 +1734,12 @@ class TestArray < Test::Unit::TestCase
end
def test_min
assert_equal(3, [3].min)
assert_equal(1, [1, 2, 3, 1, 2].min)
assert_equal(3, [1, 2, 3, 1, 2].min {|a,b| b <=> a })
cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
assert_equal([3, 2], [1, 2, 3, 1, 2].each_with_index.min(&cond))
assert_equal(1.0, [3.0, 1.0, 2.0].min)
ary = %w(albatross dog horse)
assert_equal("albatross", ary.min)
assert_equal("dog", ary.min {|a,b| a.length <=> b.length })