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

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 (± 2.2%) i/s -     28.611k in   5.028861s
Enumerable#min_by(n)      8.496k (± 0.5%) i/s -     43.146k in   5.078394s
   Enumerable#min_by      8.678k (± 0.5%) i/s -     43.911k in   5.060128s
Enumerable#max_by(n)      3.306k (± 3.0%) i/s -     16.562k in   5.014727s
   Enumerable#max_by      8.322k (± 2.8%) i/s -     42.400k in   5.099400s
Enumerable#minmax_by      6.769k (± 2.6%) i/s -     34.100k in   5.041354s

### After
  Enumerable#sort_by      8.591k (± 3.0%) i/s -     43.316k in   5.046836s
Enumerable#min_by(n)     11.489k (± 1.2%) i/s -     57.732k in   5.025504s
   Enumerable#min_by     11.835k (± 2.7%) i/s -     60.150k in   5.086450s
Enumerable#max_by(n)      5.322k (± 1.1%) i/s -     26.650k in   5.008289s
   Enumerable#max_by     11.705k (± 0.6%) i/s -     59.262k in   5.062997s
Enumerable#minmax_by     11.323k (± 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

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58968 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
watson1978 2017-05-30 09:00:56 +00:00
parent 492c88dc3b
commit cf02692f3e

22
enum.c
View file

@ -1010,6 +1010,7 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data))
static int static int
sort_by_cmp(const void *ap, const void *bp, void *data) sort_by_cmp(const void *ap, const void *bp, void *data)
{ {
struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE a; VALUE a;
VALUE b; VALUE b;
VALUE ary = (VALUE)data; VALUE ary = (VALUE)data;
@ -1021,7 +1022,7 @@ sort_by_cmp(const void *ap, const void *bp, void *data)
a = *(VALUE *)ap; a = *(VALUE *)ap;
b = *(VALUE *)bp; 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 {
static int static int
nmin_cmp(const void *ap, const void *bp, void *_data) 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; struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp; VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
VALUE cmp = rb_funcall(a, id_cmp, 1, b);
if (RBASIC(data->buf)->klass) { if (RBASIC(data->buf)->klass) {
rb_raise(rb_eRuntimeError, "%s reentered", data->method); rb_raise(rb_eRuntimeError, "%s reentered", data->method);
} }
return rb_cmpint(cmp, a, b); return OPTIMIZED_CMP(a, b, cmp_opt);
} }
static int static int
@ -1872,6 +1873,7 @@ enum_minmax(VALUE obj)
static VALUE static VALUE
min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{ {
struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args); struct MEMO *memo = MEMO_CAST(args);
VALUE v; VALUE v;
@ -1882,7 +1884,7 @@ min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
MEMO_V1_SET(memo, v); MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i); 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_V1_SET(memo, v);
MEMO_V2_SET(memo, i); MEMO_V2_SET(memo, i);
} }
@ -1933,6 +1935,7 @@ enum_min_by(int argc, VALUE *argv, VALUE obj)
static VALUE static VALUE
max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
{ {
struct cmp_opt_data cmp_opt = { 0, 0 };
struct MEMO *memo = MEMO_CAST(args); struct MEMO *memo = MEMO_CAST(args);
VALUE v; VALUE v;
@ -1943,7 +1946,7 @@ max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args))
MEMO_V1_SET(memo, v); MEMO_V1_SET(memo, v);
MEMO_V2_SET(memo, i); 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_V1_SET(memo, v);
MEMO_V2_SET(memo, i); MEMO_V2_SET(memo, i);
} }
@ -2048,6 +2051,8 @@ struct minmax_by_t {
static void static void
minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *memo) 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) { if (memo->min_bv == Qundef) {
memo->min_bv = v1; memo->min_bv = v1;
memo->max_bv = v2; memo->max_bv = v2;
@ -2055,11 +2060,11 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m
memo->max = i2; memo->max = i2;
} }
else { 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_bv = v1;
memo->min = i1; 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_bv = v2;
memo->max = i2; memo->max = i2;
} }
@ -2069,6 +2074,7 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m
static VALUE static VALUE
minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) 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); struct minmax_by_t *memo = MEMO_FOR(struct minmax_by_t, _memo);
VALUE vi, vj, j; VALUE vi, vj, j;
int n; int n;
@ -2086,7 +2092,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo))
j = memo->last; j = memo->last;
memo->last_bv = Qundef; 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) { if (n == 0) {
i = j; i = j;
vi = vj; vi = vj;