Optimize Range#count by using range_size if possible

This commit is contained in:
Kouhei Yanagita 2023-10-05 00:19:55 +09:00 committed by GitHub
parent e0c66b4749
commit 6ae2996e29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 7 deletions

11
benchmark/range_count.yml Normal file
View File

@ -0,0 +1,11 @@
prelude: |
r_1 = 1..1
r_1k = 1..1000
r_1m = 1..1000000
r_str = 'a'..'z'
benchmark:
'int 1': r_1.count
'int 1K': r_1k.count
'int 1M': r_1m.count
string: r_str.count

23
range.c
View File

@ -607,6 +607,10 @@ double_as_int64(double d)
static int static int
is_integer_p(VALUE v) is_integer_p(VALUE v)
{ {
if (rb_integer_type_p(v)) {
return true;
}
ID id_integer_p; ID id_integer_p;
VALUE is_int; VALUE is_int;
CONST_ID(id_integer_p, "integer?"); CONST_ID(id_integer_p, "integer?");
@ -2166,17 +2170,22 @@ range_count(int argc, VALUE *argv, VALUE range)
* Infinity. Just let it loop. */ * Infinity. Just let it loop. */
return rb_call_super(argc, argv); return rb_call_super(argc, argv);
} }
else if (NIL_P(RANGE_END(range))) {
VALUE beg = RANGE_BEG(range), end = RANGE_END(range);
if (NIL_P(beg) || NIL_P(end)) {
/* We are confident that the answer is Infinity. */ /* We are confident that the answer is Infinity. */
return DBL2NUM(HUGE_VAL); return DBL2NUM(HUGE_VAL);
} }
else if (NIL_P(RANGE_BEG(range))) {
/* We are confident that the answer is Infinity. */ if (is_integer_p(beg)) {
return DBL2NUM(HUGE_VAL); VALUE size = range_size(range);
} if (!NIL_P(size)) {
else { return size;
return rb_call_super(argc, argv); }
} }
return rb_call_super(argc, argv);
} }
static bool static bool

View File

@ -1075,7 +1075,17 @@ class TestRange < Test::Unit::TestCase
end end
def test_count def test_count
assert_equal 42, (1..42).count
assert_equal 41, (1...42).count
assert_equal 0, (42..1).count
assert_equal 0, (42...1).count
assert_equal 2**100, (1..2**100).count
assert_equal 6, (1...6.3).count
assert_equal 4, ('a'..'d').count
assert_equal 3, ('a'...'d').count
assert_equal(Float::INFINITY, (1..).count) assert_equal(Float::INFINITY, (1..).count)
assert_equal(Float::INFINITY, (..1).count)
end end
def test_overlap? def test_overlap?