Optimize Range#bsearch for beginless/endless ranges within Fixnum

This commit is contained in:
Kouhei Yanagita 2023-09-19 15:29:35 +09:00 committed by Nobuyoshi Nakada
parent ab637cad2b
commit 7e350f5310
3 changed files with 44 additions and 15 deletions

View File

@ -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 }

32
range.c
View File

@ -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;

View File

@ -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 {} }