[前][次][番号順一覧][スレッド一覧]

ruby-changes:67001

From: Jeremy <ko1@a...>
Date: Fri, 30 Jul 2021 07:19:32 +0900 (JST)
Subject: [ruby-changes:67001] 9931e2f509 (master): Improve performance of Integer#digits

https://git.ruby-lang.org/ruby.git/commit/?id=9931e2f509

From 9931e2f5091e95dd947de3b3a00167ae2fd5194a Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 17 Jun 2021 11:27:53 -0700
Subject: Improve performance of Integer#digits

This speeds up performance by multiple orders of magnitude for
large integers.

Fixes [Bug #14391]

Co-authored-by: tompng (tomoya ishida) <tomoyapenguin@g...>
---
 numeric.c                 | 33 +++++++++++++++++++++++++++------
 test/ruby/test_integer.rb |  2 ++
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/numeric.c b/numeric.c
index 5564a6e..c60853f 100644
--- a/numeric.c
+++ b/numeric.c
@@ -4805,7 +4805,7 @@ rb_fix_digits(VALUE fix, long base) https://github.com/ruby/ruby/blob/trunk/numeric.c#L4805
 static VALUE
 rb_int_digits_bigbase(VALUE num, VALUE base)
 {
-    VALUE digits;
+    VALUE digits, bases;
 
     assert(!rb_num_negative_p(num));
 
@@ -4823,11 +4823,32 @@ rb_int_digits_bigbase(VALUE num, VALUE base) https://github.com/ruby/ruby/blob/trunk/numeric.c#L4823
     if (FIXNUM_P(num))
         return rb_ary_new_from_args(1, num);
 
-    digits = rb_ary_new();
-    while (!FIXNUM_P(num) || FIX2LONG(num) > 0) {
-        VALUE qr = rb_int_divmod(num, base);
-        rb_ary_push(digits, RARRAY_AREF(qr, 1));
-        num = RARRAY_AREF(qr, 0);
+    if (int_lt(rb_int_div(rb_int_bit_length(num), rb_int_bit_length(base)), INT2FIX(50))) {
+        digits = rb_ary_new();
+        while (!FIXNUM_P(num) || FIX2LONG(num) > 0) {
+            VALUE qr = rb_int_divmod(num, base);
+            rb_ary_push(digits, RARRAY_AREF(qr, 1));
+            num = RARRAY_AREF(qr, 0);
+        }
+        return digits;
+    }
+
+    bases = rb_ary_new();
+    for (VALUE b = base; int_lt(b, num) == Qtrue; b = rb_int_mul(b, b)) {
+        rb_ary_push(bases, b);
+    }
+    digits = rb_ary_new_from_args(1, num);
+    while (RARRAY_LEN(bases)) {
+        VALUE b = rb_ary_pop(bases);
+        long i, last_idx = RARRAY_LEN(digits) - 1;
+        for(i = last_idx; i >= 0; i--) {
+            VALUE n = RARRAY_AREF(digits, i);
+            VALUE divmod = rb_int_divmod(n, b);
+            VALUE div = RARRAY_AREF(divmod, 0);
+            VALUE mod = RARRAY_AREF(divmod, 1);
+            if (i != last_idx || div != INT2FIX(0)) rb_ary_store(digits, 2 * i + 1,  div);
+            rb_ary_store(digits, 2 * i, mod);
+        }
     }
 
     return digits;
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 1cd256a..9354514 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -576,6 +576,8 @@ class TestInteger < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_integer.rb#L576
     assert_equal([0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1234567890.digits)
     assert_equal([90, 78, 56, 34, 12], 1234567890.digits(100))
     assert_equal([10, 5, 6, 8, 0, 10, 8, 6, 1], 1234567890.digits(13))
+    assert_equal((2 ** 1024).to_s(7).chars.map(&:to_i).reverse, (2 ** 1024).digits(7))
+    assert_equal([0] * 100 + [1], (2 ** (128 * 100)).digits(2 ** 128))
   end
 
   def test_digits_for_negative_numbers
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]