Skip to content

Commit a66e4f2

Browse files
authored
Improve performance of bignum[beg, len] (ruby#14007)
Implement rb_big_aref2. Taking a small slice from large bignum was slow in rb_int_aref2.
1 parent 46d106f commit a66e4f2

File tree

5 files changed

+131
-18
lines changed

5 files changed

+131
-18
lines changed

bignum.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6757,6 +6757,73 @@ rb_big_aref(VALUE x, VALUE y)
67576757
return (xds[s1] & bit) ? INT2FIX(1) : INT2FIX(0);
67586758
}
67596759

6760+
VALUE
6761+
rb_big_aref2(VALUE x, VALUE beg, VALUE len)
6762+
{
6763+
BDIGIT *xds, *vds;
6764+
VALUE v;
6765+
size_t copy_begin, xn, shift;
6766+
ssize_t begin, length, end;
6767+
bool negative_add_one;
6768+
6769+
beg = rb_to_int(beg);
6770+
len = rb_to_int(len);
6771+
length = NUM2SSIZET(len);
6772+
begin = NUM2SSIZET(beg);
6773+
end = NUM2SSIZET(rb_int_plus(beg, len));
6774+
shift = begin < 0 ? -begin : 0;
6775+
xn = BIGNUM_LEN(x);
6776+
xds = BDIGITS(x);
6777+
6778+
if (length < 0) return rb_big_rshift(x, beg);
6779+
if (length == 0 || end <= 0) return INT2FIX(0);
6780+
if (begin < 0) begin = 0;
6781+
6782+
if ((size_t)(end - 1) / BITSPERDIG >= xn) {
6783+
/* end > xn * BITSPERDIG */
6784+
end = xn * BITSPERDIG;
6785+
}
6786+
6787+
if ((size_t)begin / BITSPERDIG < xn) {
6788+
/* begin < xn * BITSPERDIG */
6789+
size_t shift_bits, copy_end;
6790+
copy_begin = begin / BITSPERDIG;
6791+
shift_bits = begin % BITSPERDIG;
6792+
copy_end = (end - 1) / BITSPERDIG + 1;
6793+
v = bignew(copy_end - copy_begin, 1);
6794+
vds = BDIGITS(v);
6795+
MEMCPY(vds, xds + copy_begin, BDIGIT, copy_end - copy_begin);
6796+
negative_add_one = (vds[0] & ((1 << shift_bits) - 1)) == 0;
6797+
v = bignorm(v);
6798+
if (shift_bits) v = rb_int_rshift(v, SIZET2NUM(shift_bits));
6799+
}
6800+
else {
6801+
/* Out of range */
6802+
v = INT2FIX(0);
6803+
negative_add_one = false;
6804+
copy_begin = begin = end = 0;
6805+
}
6806+
6807+
if (BIGNUM_NEGATIVE_P(x)) {
6808+
size_t mask_size = length - shift;
6809+
VALUE mask = rb_int_minus(rb_int_lshift(INT2FIX(1), SIZET2NUM(mask_size)), INT2FIX(1));
6810+
v = rb_int_xor(v, mask);
6811+
for (size_t i = 0; negative_add_one && i < copy_begin; i++) {
6812+
if (xds[i]) negative_add_one = false;
6813+
}
6814+
if (negative_add_one) v = rb_int_plus(v, INT2FIX(1));
6815+
v = rb_int_and(v, mask);
6816+
}
6817+
else {
6818+
size_t mask_size = (size_t)end - begin;
6819+
VALUE mask = rb_int_minus(rb_int_lshift(INT2FIX(1), SIZET2NUM(mask_size)), INT2FIX(1));
6820+
v = rb_int_and(v, mask);
6821+
}
6822+
RB_GC_GUARD(x);
6823+
if (shift) v = rb_int_lshift(v, SSIZET2NUM(shift));
6824+
return v;
6825+
}
6826+
67606827
VALUE
67616828
rb_big_hash(VALUE x)
67626829
{

internal/bignum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ VALUE rb_integer_float_eq(VALUE x, VALUE y);
121121
VALUE rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception);
122122
VALUE rb_big_comp(VALUE x);
123123
VALUE rb_big_aref(VALUE x, VALUE y);
124+
VALUE rb_big_aref2(VALUE num, VALUE beg, VALUE len);
124125
VALUE rb_big_abs(VALUE x);
125126
VALUE rb_big_size_m(VALUE big);
126127
VALUE rb_big_bit_length(VALUE big);

internal/numeric.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ VALUE rb_int_cmp(VALUE x, VALUE y);
8585
VALUE rb_int_equal(VALUE x, VALUE y);
8686
VALUE rb_int_divmod(VALUE x, VALUE y);
8787
VALUE rb_int_and(VALUE x, VALUE y);
88+
VALUE rb_int_xor(VALUE x, VALUE y);
8889
VALUE rb_int_lshift(VALUE x, VALUE y);
8990
VALUE rb_int_rshift(VALUE x, VALUE y);
9091
VALUE rb_int_div(VALUE x, VALUE y);

numeric.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5115,8 +5115,8 @@ fix_xor(VALUE x, VALUE y)
51155115
*
51165116
*/
51175117

5118-
static VALUE
5119-
int_xor(VALUE x, VALUE y)
5118+
VALUE
5119+
rb_int_xor(VALUE x, VALUE y)
51205120
{
51215121
if (FIXNUM_P(x)) {
51225122
return fix_xor(x, y);
@@ -5288,10 +5288,23 @@ generate_mask(VALUE len)
52885288
return rb_int_minus(rb_int_lshift(INT2FIX(1), len), INT2FIX(1));
52895289
}
52905290

5291+
static VALUE
5292+
int_aref2(VALUE num, VALUE beg, VALUE len)
5293+
{
5294+
if (RB_TYPE_P(num, T_BIGNUM)) {
5295+
return rb_big_aref2(num, beg, len);
5296+
}
5297+
else {
5298+
num = rb_int_rshift(num, beg);
5299+
VALUE mask = generate_mask(len);
5300+
return rb_int_and(num, mask);
5301+
}
5302+
}
5303+
52915304
static VALUE
52925305
int_aref1(VALUE num, VALUE arg)
52935306
{
5294-
VALUE orig_num = num, beg, end;
5307+
VALUE beg, end;
52955308
int excl;
52965309

52975310
if (rb_range_values(arg, &beg, &end, &excl)) {
@@ -5311,22 +5324,19 @@ int_aref1(VALUE num, VALUE arg)
53115324
return INT2FIX(0);
53125325
}
53135326
}
5314-
num = rb_int_rshift(num, beg);
53155327

53165328
int cmp = compare_indexes(beg, end);
53175329
if (!NIL_P(end) && cmp < 0) {
53185330
VALUE len = rb_int_minus(end, beg);
53195331
if (!excl) len = rb_int_plus(len, INT2FIX(1));
5320-
VALUE mask = generate_mask(len);
5321-
num = rb_int_and(num, mask);
5332+
return int_aref2(num, beg, len);
53225333
}
53235334
else if (cmp == 0) {
53245335
if (excl) return INT2FIX(0);
5325-
num = orig_num;
53265336
arg = beg;
53275337
goto one_bit;
53285338
}
5329-
return num;
5339+
return rb_int_rshift(num, beg);
53305340
}
53315341

53325342
one_bit:
@@ -5339,15 +5349,6 @@ int_aref1(VALUE num, VALUE arg)
53395349
return Qnil;
53405350
}
53415351

5342-
static VALUE
5343-
int_aref2(VALUE num, VALUE beg, VALUE len)
5344-
{
5345-
num = rb_int_rshift(num, beg);
5346-
VALUE mask = generate_mask(len);
5347-
num = rb_int_and(num, mask);
5348-
return num;
5349-
}
5350-
53515352
/*
53525353
* call-seq:
53535354
* self[offset] -> 0 or 1
@@ -6366,7 +6367,7 @@ Init_Numeric(void)
63666367

63676368
rb_define_method(rb_cInteger, "&", rb_int_and, 1);
63686369
rb_define_method(rb_cInteger, "|", int_or, 1);
6369-
rb_define_method(rb_cInteger, "^", int_xor, 1);
6370+
rb_define_method(rb_cInteger, "^", rb_int_xor, 1);
63706371
rb_define_method(rb_cInteger, "[]", int_aref, -1);
63716372

63726373
rb_define_method(rb_cInteger, "<<", rb_int_lshift, 1);

test/ruby/test_bignum.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,49 @@ def test_aref
605605
assert_equal(1, (-2**(BIGNUM_MIN_BITS*4))[BIGNUM_MIN_BITS*4])
606606
end
607607

608+
def test_aref2
609+
x = (0x123456789abcdef << (BIGNUM_MIN_BITS + 32)) | 0x12345678
610+
assert_equal(x, x[0, x.bit_length])
611+
assert_equal(x >> 10, x[10, x.bit_length])
612+
assert_equal(0x45678, x[0, 20])
613+
assert_equal(0x6780, x[-4, 16])
614+
assert_equal(0x123456, x[x.bit_length - 21, 40])
615+
assert_equal(0x6789ab, x[x.bit_length - 41, 24])
616+
assert_equal(0, x[-20, 10])
617+
assert_equal(0, x[x.bit_length + 10, 10])
618+
619+
assert_equal(0, x[5, 0])
620+
assert_equal(0, (-x)[5, 0])
621+
622+
assert_equal(x >> 5, x[5, -1])
623+
assert_equal(x << 5, x[-5, -1])
624+
assert_equal((-x) >> 5, (-x)[5, -1])
625+
assert_equal((-x) << 5, (-x)[-5, -1])
626+
627+
assert_equal(x << 5, x[-5, FIXNUM_MAX])
628+
assert_equal(x >> 5, x[5, FIXNUM_MAX])
629+
assert_equal(0, x[FIXNUM_MIN, 100])
630+
assert_equal(0, (-x)[FIXNUM_MIN, 100])
631+
632+
y = (x << 160) | 0x1234_0000_0000_0000_1234_0000_0000_0000
633+
assert_equal(0xffffedcc00, (-y)[40, 40])
634+
assert_equal(0xfffffffedc, (-y)[52, 40])
635+
assert_equal(0xffffedcbff, (-y)[104, 40])
636+
assert_equal(0xfffff6e5d4, (-y)[y.bit_length - 20, 40])
637+
assert_equal(0, (-y)[-20, 10])
638+
assert_equal(0xfff, (-y)[y.bit_length + 10, 12])
639+
640+
z = (1 << (BIGNUM_MIN_BITS * 2)) - 1
641+
assert_equal(0x400, (-z)[-10, 20])
642+
assert_equal(1, (-z)[0, 20])
643+
assert_equal(0, (-z)[10, 20])
644+
assert_equal(1, (-z)[0, z.bit_length])
645+
assert_equal(0, (-z)[z.bit_length - 10, 10])
646+
assert_equal(0x400, (-z)[z.bit_length - 10, 11])
647+
assert_equal(0xfff, (-z)[z.bit_length, 12])
648+
assert_equal(0xfff00, (-z)[z.bit_length - 8, 20])
649+
end
650+
608651
def test_hash
609652
assert_nothing_raised { T31P.hash }
610653
end

0 commit comments

Comments
 (0)