From f4b313f7338f5fbe37f73aae29f70aeb474f7f5b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 2 Jul 2024 18:29:23 -0700 Subject: [PATCH] move Integer#downto to Ruby Speeds up ChunkyPNG. The interpreter is about 70% faster: ``` before: ruby 3.4.0dev (2024-07-03T15:16:17Z master 786cf9db48) [arm64-darwin23] after: ruby 3.4.0dev (2024-07-03T15:32:25Z ruby-downto 0b8b744ce2) [arm64-darwin23] ---------- ----------- ---------- ---------- ---------- ------------- ------------ bench before (ms) stddev (%) after (ms) stddev (%) after 1st itr before/after chunky-png 892.2 0.1 526.3 1.0 1.65 1.70 ---------- ----------- ---------- ---------- ---------- ------------- ------------ Legend: - after 1st itr: ratio of before/after time for the first benchmarking iteration. - before/after: ratio of before/after time. Higher is better for after. Above 1 represents a speedup. ``` YJIT is 2.5x faster: ``` before: ruby 3.4.0dev (2024-07-03T15:16:17Z master 786cf9db48) +YJIT [arm64-darwin23] after: ruby 3.4.0dev (2024-07-03T15:32:25Z ruby-downto 0b8b744ce2) +YJIT [arm64-darwin23] ---------- ----------- ---------- ---------- ---------- ------------- ------------ bench before (ms) stddev (%) after (ms) stddev (%) after 1st itr before/after chunky-png 709.4 0.1 278.8 0.3 2.35 2.54 ---------- ----------- ---------- ---------- ---------- ------------- ------------ Legend: - after 1st itr: ratio of before/after time for the first benchmarking iteration. - before/after: ratio of before/after time. Higher is better for after. Above 1 represents a speedup. ``` --- numeric.c | 45 --------------------------------------------- numeric.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/numeric.c b/numeric.c index 785bdc53d7..6e5b096768 100644 --- a/numeric.c +++ b/numeric.c @@ -5623,50 +5623,6 @@ int_downto_size(VALUE from, VALUE args, VALUE eobj) return ruby_num_interval_step_size(from, RARRAY_AREF(args, 0), INT2FIX(-1), FALSE); } -/* - * call-seq: - * downto(limit) {|i| ... } -> self - * downto(limit) -> enumerator - * - * Calls the given block with each integer value from +self+ down to +limit+; - * returns +self+: - * - * a = [] - * 10.downto(5) {|i| a << i } # => 10 - * a # => [10, 9, 8, 7, 6, 5] - * a = [] - * 0.downto(-5) {|i| a << i } # => 0 - * a # => [0, -1, -2, -3, -4, -5] - * 4.downto(5) {|i| fail 'Cannot happen' } # => 4 - * - * With no block given, returns an Enumerator. - * - */ - -static VALUE -int_downto(VALUE from, VALUE to) -{ - RETURN_SIZED_ENUMERATOR(from, 1, &to, int_downto_size); - if (FIXNUM_P(from) && FIXNUM_P(to)) { - long i, end; - - end = FIX2LONG(to); - for (i=FIX2LONG(from); i >= end; i--) { - rb_yield(LONG2FIX(i)); - } - } - else { - VALUE i = from, c; - - while (!(c = rb_funcall(i, '<', 1, to))) { - rb_yield(i); - i = rb_funcall(i, '-', 1, INT2FIX(1)); - } - if (NIL_P(c)) rb_cmperr(i, to); - } - return from; -} - static VALUE int_dotimes_size(VALUE num, VALUE args, VALUE eobj) { @@ -6246,7 +6202,6 @@ Init_Numeric(void) rb_define_method(rb_cInteger, "anybits?", int_anybits_p, 1); rb_define_method(rb_cInteger, "nobits?", int_nobits_p, 1); rb_define_method(rb_cInteger, "upto", int_upto, 1); - rb_define_method(rb_cInteger, "downto", int_downto, 1); rb_define_method(rb_cInteger, "succ", int_succ, 0); rb_define_method(rb_cInteger, "next", int_succ, 0); rb_define_method(rb_cInteger, "pred", int_pred, 0); diff --git a/numeric.rb b/numeric.rb index 4dc406fd23..ebe06a7666 100644 --- a/numeric.rb +++ b/numeric.rb @@ -241,6 +241,35 @@ class Integer self end + # call-seq: + # downto(limit) {|i| ... } -> self + # downto(limit) -> enumerator + # + # Calls the given block with each integer value from +self+ down to +limit+; + # returns +self+: + # + # a = [] + # 10.downto(5) {|i| a << i } # => 10 + # a # => [10, 9, 8, 7, 6, 5] + # a = [] + # 0.downto(-5) {|i| a << i } # => 0 + # a # => [0, -1, -2, -3, -4, -5] + # 4.downto(5) {|i| fail 'Cannot happen' } # => 4 + # + # With no block given, returns an Enumerator. + def downto to + Primitive.attr! :inline_block + unless defined?(yield) + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 1, &to, int_downto_size)' + end + + from = self + while from >= to + yield from + from = from.pred + end + end + # call-seq: # to_i -> self #