From 7e350f53107533856b0a87c26e5a1e5505ea5d59 Mon Sep 17 00:00:00 2001 From: Kouhei Yanagita Date: Tue, 19 Sep 2023 15:29:35 +0900 Subject: [PATCH] Optimize Range#bsearch for beginless/endless ranges within Fixnum --- benchmark/range_bsearch.yml | 24 ++++++++++++++++++------ range.c | 32 +++++++++++++++++++++++--------- test/ruby/test_range.rb | 3 +++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/benchmark/range_bsearch.yml b/benchmark/range_bsearch.yml index 9a7388ab04..8d7bedb662 100644 --- a/benchmark/range_bsearch.yml +++ b/benchmark/range_bsearch.yml @@ -1,9 +1,21 @@ prelude: | - r = (1..) + re = (1..) + rb = (..0) benchmark: - '10**1': r.bsearch { |x| x >= 10 } - '10**2': r.bsearch { |x| x >= 100 } - '10**3': r.bsearch { |x| x >= 1000 } - '10**4': r.bsearch { |x| x >= 10000 } - '10**5': r.bsearch { |x| x >= 100000 } + 'endless 10**0': re.bsearch { |x| x >= 1 } + 'endless 10**1': re.bsearch { |x| x >= 10 } + 'endless 10**2': re.bsearch { |x| x >= 100 } + 'endless 10**3': re.bsearch { |x| x >= 1000 } + 'endless 10**4': re.bsearch { |x| x >= 10000 } + 'endless 10**5': re.bsearch { |x| x >= 100000 } + 'endless 10**10': re.bsearch { |x| x >= 10000000000 } + 'endless 10**100': re.bsearch { |x| x >= 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 } + 'beginless -10**0': rb.bsearch { |x| x >= -1 } + 'beginless -10**1': rb.bsearch { |x| x >= -10 } + 'beginless -10**2': rb.bsearch { |x| x >= -100 } + 'beginless -10**3': rb.bsearch { |x| x >= -1000 } + 'beginless -10**4': rb.bsearch { |x| x >= -10000 } + 'beginless -10**5': rb.bsearch { |x| x >= -100000 } + 'beginless -10**10': rb.bsearch { |x| x >= -10000000000 } + 'beginless -10**100': rb.bsearch { |x| x >= -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 } diff --git a/range.c b/range.c index 7c116d78c7..a8bdb98b53 100644 --- a/range.c +++ b/range.c @@ -703,10 +703,10 @@ range_bsearch(VALUE range) * (-1...0.0).bsearch to yield -0.0. */ -#define BSEARCH(conv) \ +#define BSEARCH(conv, excl) \ do { \ RETURN_ENUMERATOR(range, 0, 0); \ - if (EXCL(range)) high--; \ + if (excl) high--; \ org_high = high; \ while (low < high) { \ mid = ((high < 0) == (low < 0)) ? low + ((high - low) / 2) \ @@ -726,22 +726,26 @@ range_bsearch(VALUE range) return satisfied; \ } while (0) +#define BSEARCH_FIXNUM(beg, end, excl) \ + do { \ + long low = FIX2LONG(beg); \ + long high = FIX2LONG(end); \ + long mid, org_high; \ + BSEARCH(INT2FIX, (excl)); \ + } while (0) beg = RANGE_BEG(range); end = RANGE_END(range); if (FIXNUM_P(beg) && FIXNUM_P(end)) { - long low = FIX2LONG(beg); - long high = FIX2LONG(end); - long mid, org_high; - BSEARCH(INT2FIX); + BSEARCH_FIXNUM(beg, end, EXCL(range)); } #if SIZEOF_DOUBLE == 8 && defined(HAVE_INT64_T) else if (RB_FLOAT_TYPE_P(beg) || RB_FLOAT_TYPE_P(end)) { int64_t low = double_as_int64(NIL_P(beg) ? -HUGE_VAL : RFLOAT_VALUE(rb_Float(beg))); int64_t high = double_as_int64(NIL_P(end) ? HUGE_VAL : RFLOAT_VALUE(rb_Float(end))); int64_t mid, org_high; - BSEARCH(int64_as_double_to_num); + BSEARCH(int64_as_double_to_num, EXCL(range)); } #endif else if (is_integer_p(beg) && is_integer_p(end)) { @@ -755,7 +759,12 @@ range_bsearch(VALUE range) VALUE mid = rb_funcall(beg, '+', 1, diff); BSEARCH_CHECK(mid); if (smaller) { - return bsearch_integer_range(beg, mid, 0); + if (FIXNUM_P(beg) && FIXNUM_P(mid)) { + BSEARCH_FIXNUM(beg, mid, false); + } + else { + return bsearch_integer_range(beg, mid, false); + } } diff = rb_funcall(diff, '*', 1, LONG2FIX(2)); beg = mid; @@ -768,7 +777,12 @@ range_bsearch(VALUE range) VALUE mid = rb_funcall(end, '+', 1, diff); BSEARCH_CHECK(mid); if (!smaller) { - return bsearch_integer_range(mid, end, 0); + if (FIXNUM_P(mid) && FIXNUM_P(end)) { + BSEARCH_FIXNUM(mid, end, false); + } + else { + return bsearch_integer_range(mid, end, false); + } } diff = rb_funcall(diff, '*', 1, LONG2FIX(2)); end = mid; diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb index 8c310b0bae..4d2b6294ed 100644 --- a/test/ruby/test_range.rb +++ b/test/ruby/test_range.rb @@ -1047,7 +1047,10 @@ class TestRange < Test::Unit::TestCase assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 }) assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true }) assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false }) + + assert_equal(bignum * 2 + 1, (0...).bsearch {|i| i > bignum * 2 }) assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 }) + assert_equal(-bignum * 2 + 1, (...0).bsearch {|i| i > -bignum * 2 }) assert_equal(-bignum * 2 + 1, (...-bignum).bsearch {|i| i > -bignum * 2 }) assert_raise(TypeError) { ("a".."z").bsearch {} }