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

ruby-changes:64847

From: Kenta <ko1@a...>
Date: Wed, 13 Jan 2021 08:14:41 +0900 (JST)
Subject: [ruby-changes:64847] 4ba3a4491e (master): [ruby/bigdecimal] Optimize rb_float_convert_to_BigDecimal by using dtoa

https://git.ruby-lang.org/ruby.git/commit/?id=4ba3a4491e

From 4ba3a4491e15ed50f26c8f7a602fb4ef5452085a Mon Sep 17 00:00:00 2001
From: Kenta Murata <mrkn@m...>
Date: Tue, 12 Jan 2021 22:58:17 +0900
Subject: [ruby/bigdecimal] Optimize rb_float_convert_to_BigDecimal by using
 dtoa

This improve the conversion speed several times faster than before.

```
RUBYLIB= BUNDLER_ORIG_RUBYLIB= /home/mrkn/.rbenv/versions/3.0.0/bin/ruby -v -S benchmark-driver /home/mrkn/src/github.com/ruby/bigdecimal/benchmark/from_float.yml
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
Calculating -------------------------------------
                     bigdecimal 3.0.0      master
              flt_e0         156.400k    783.356k i/s -    100.000k times in 0.639388s 0.127656s
            flt_ep10         158.640k    777.978k i/s -    100.000k times in 0.630359s 0.128538s
           flt_ep100         101.676k    504.259k i/s -    100.000k times in 0.983512s 0.198311s
            flt_em10         103.439k    726.339k i/s -    100.000k times in 0.966751s 0.137677s
           flt_em100          79.675k    651.446k i/s -    100.000k times in 1.255095s 0.153505s

Comparison:
                           flt_e0
              master:    783355.6 i/s
    bigdecimal 3.0.0:    156399.5 i/s - 5.01x  slower

                         flt_ep10
              master:    777977.6 i/s
    bigdecimal 3.0.0:    158639.7 i/s - 4.90x  slower

                        flt_ep100
              master:    504259.4 i/s
    bigdecimal 3.0.0:    101676.5 i/s - 4.96x  slower

                         flt_em10
              master:    726338.6 i/s
    bigdecimal 3.0.0:    103439.2 i/s - 7.02x  slower

                        flt_em100
              master:    651446.3 i/s
    bigdecimal 3.0.0:     79675.3 i/s - 8.18x  slower

```

https://github.com/ruby/bigdecimal/commit/5bdaedd530
https://github.com/ruby/bigdecimal/commit/9bfff57f90
https://github.com/ruby/bigdecimal/commit/d071a0abbb

diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
index 4997487..2d3f09d 100644
--- a/ext/bigdecimal/bigdecimal.c
+++ b/ext/bigdecimal/bigdecimal.c
@@ -74,7 +74,7 @@ static ID id_half; https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L74
 #define BASE1 (BASE/10)
 
 #ifndef DBLE_FIG
-#define DBLE_FIG rmpd_double_figures()    /* figure of double */
+#define DBLE_FIG RMPD_DOUBLE_FIGURES  /* figure of double */
 #endif
 
 #define LOG10_2 0.3010299956639812
@@ -205,6 +205,7 @@ cannot_be_coerced_into_BigDecimal(VALUE exc_class, VALUE v) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L205
 }
 
 static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
+static VALUE rb_inum_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
 static VALUE rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
 static VALUE rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
 static VALUE rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception);
@@ -2824,8 +2825,118 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2825
         rb_raise(rb_eArgError, "precision too large.");
     }
 
-    val = rb_funcall(val, id_to_r, 0);
-    return rb_rational_convert_to_BigDecimal(val, digs, raise_exception);
+    /* Use the same logic in flo_to_s to convert a float to a decimal string */
+    char buf[DBLE_FIG + BASE_FIG + 2 + 1];
+    int decpt, negative_p;
+    char *e;
+    char *p = BigDecimal_dtoa(d, 2, digs, &decpt, &negative_p, &e);
+    int len10 = (int)(e - p);
+    if (len10 >= (int)sizeof(buf))
+        len10 = (int)sizeof(buf) - 1;
+    memcpy(buf, p, len10);
+    xfree(p);
+
+    VALUE inum;
+    size_t RB_UNUSED_VAR(prec) = 0;
+    size_t exp = 0;
+    if (decpt > 0) {
+        if (decpt < len10) {
+            /*
+             *     len10 |---------------|
+             *           :       |-------| frac_len10 = len10 - decpt
+             *     decpt |-------|       |--| ntz10 = BASE_FIG - frac_len10 % BASE_FIG
+             *           :       :       :
+             *         00 dd dddd.dddd dd 00
+             *   prec |-----.----.----.-----| prec = exp + roomof(frac_len, BASE_FIG)
+             *   exp  |-----.----| exp = roomof(decpt, BASE_FIG)
+             */
+            const size_t frac_len10 = len10 - decpt;
+            const size_t ntz10 = BASE_FIG - frac_len10 % BASE_FIG;
+            memset(buf + len10, '0', ntz10);
+            buf[len10 + ntz10] = '\0';
+            inum = rb_cstr_to_inum(buf, 10, false);
+
+            exp = roomof(decpt, BASE_FIG);
+            prec = exp + roomof(frac_len10, BASE_FIG);
+        }
+        else {
+            /*
+             *      decpt |-----------------------|
+             *      len10 |----------|            :
+             *            :          |------------| exp10
+             *            :          :            :
+             *          00 dd dddd dd 00 0000 0000.0
+             *         :             :  :         :
+             *         :             |--| ntz10 = exp10 % BASE_FIG
+             *    prec |-----.----.-----|         :
+             *         :                |----.----| exp10 / BASE_FIG
+             *     exp |-----.----.-----.----.----|
+             */
+            const size_t exp10 = decpt - len10;
+            const size_t ntz10 = exp10 % BASE_FIG;
+
+            memset(buf + len10, '0', ntz10);
+            buf[len10 + ntz10] = '\0';
+            inum = rb_cstr_to_inum(buf, 10, false);
+
+            prec = roomof(len10 + ntz10, BASE_FIG);
+            exp = prec + exp10 / BASE_FIG;
+        }
+    }
+    else if (decpt == 0) {
+        /*
+         *   len10 |------------|
+         *         :            :
+         *        0.dddd dddd dd 00
+         *         :            :  :
+         *         :            |--| ntz10 = prec * BASE_FIG - len10
+         *    prec |----.----.-----| roomof(len10, BASE_FIG)
+         */
+        prec = roomof(len10, BASE_FIG);
+        const size_t ntz10 = prec * BASE_FIG - len10;
+
+        memset(buf + len10, '0', ntz10);
+        buf[len10 + ntz10] = '\0';
+        inum = rb_cstr_to_inum(buf, 10, false);
+    }
+    else {
+        /*
+         *           len10 |---------------|
+         *                 :               :
+         *   decpt |-------|               |--| ntz10 = prec * BASE_FIG - nlz10 - len10
+         *         :       :               :
+         *        0.0000 00 dd dddd dddd dd 00
+         *              :  :                  :
+         *        nlz10 |--|                  : decpt % BASE_FIG
+         *         prec |-----.----.----.-----| roomof(decpt + len10, BASE_FIG) - exp
+         *    exp  |----| decpt / BASE_FIG
+         */
+        decpt = -decpt;
+
+        const size_t nlz10 = decpt % BASE_FIG;
+        exp = decpt / BASE_FIG;
+        prec = roomof(decpt + len10, BASE_FIG) - exp;
+        const size_t ntz10 = prec * BASE_FIG - nlz10 - len10;
+
+        if (nlz10 > 0) {
+            memmove(buf + nlz10, buf, len10);
+            memset(buf, '0', nlz10);
+        }
+        memset(buf + nlz10 + len10, '0', ntz10);
+        buf[nlz10 + len10 + ntz10] = '\0';
+        inum = rb_cstr_to_inum(buf, 10, false);
+
+        exp = -exp;
+    }
+
+    VALUE bd = rb_inum_convert_to_BigDecimal(inum, SIZE_MAX, raise_exception);
+    Real *vp;
+    TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
+    assert(vp->Prec == prec);
+    vp->exponent = exp;
+
+    if (negative_p) VpSetSign(vp, -1);
+    return bd;
 }
 
 static VALUE
diff --git a/ext/bigdecimal/bigdecimal.gemspec b/ext/bigdecimal/bigdecimal.gemspec
index 8a2f5b6..79009aa 100644
--- a/ext/bigdecimal/bigdecimal.gemspec
+++ b/ext/bigdecimal/bigdecimal.gemspec
@@ -21,7 +21,9 @@ Gem::Specification.new do |s| https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.gemspec#L21
     ext/bigdecimal/bigdecimal.h
     ext/bigdecimal/bits.h
     ext/bigdecimal/feature.h
+    ext/bigdecimal/missing.c
     ext/bigdecimal/missing.h
+    ext/bigdecimal/missing/dtoa.c
     ext/bigdecimal/static_assert.h
     lib/bigdecimal.rb
     lib/bigdecimal/jacobian.rb
@@ -40,5 +42,5 @@ Gem::Specification.new do |s| https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.gemspec#L42
   s.add_development_dependency "rake", ">= 12.3.3"
   s.add_development_dependency "rake-compiler", ">= 0.9"
   s.add_development_dependency "minitest", "< 5.0.0"
-  s.add_development_dependency "pry"
+  s.add_development_dependency "irb"
 end
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
index 6b6ac21..5f343db 100644
--- a/ext/bigdecimal/bigdecimal.h
+++ b/ext/bigdecimal/bigdecimal.h
@@ -77,6 +77,7 @@ extern VALUE rb_cBigDecimal; https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.h#L77
 # define RMPD_BASE ((DECDIG)100U)
 #endif
 
+#define RMPD_DOUBLE_FIGURES (1+DBL_DIG)
 
 /*
  *  NaN & Infinity
@@ -175,7 +176,7 @@ rmpd_base_value(void) { return RMPD_BASE; } https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.h#L176
 static inline size_t
 rmpd_component_figures(void) { return RMPD_COMPONENT_FIGURES; }
 static inline size_t
-rmpd_double_figures(void) { return 1+DBL_DIG; }
+rmpd_double_figures(void) { return RMPD_DOUBLE_FIGURES; }
 
 #define VpBaseFig() rmpd_component_figures()
 #define VpDblFig() rmpd_double_figures()
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
index d5140e8..c92aacb 100644
--- a/ext/bigdecimal/extconf.rb
+++ b/ext/bigdecimal/extconf.rb
@@ -66,6 +66,7 @@ have_func("llabs", "stdlib.h") https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/extconf.rb#L66
 have_func("finite", "math.h")
 have_func("isfinite", "math.h")
 
+have_header("ruby/atomic.h")
 have_header("ruby/internal/has/builtin.h")
 have_header("ruby/internal/static_assert.h")
 
diff --git a/ext/bigdecimal/missing.c b/ext/bigdecimal/missing.c
new file mode (... truncated)

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

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