diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2018-09-03 18:37:40 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2018-09-03 18:50:34 -0700 |
commit | fe042e9d15da7863b5beb4c2cc326a62d2c7fccb (patch) | |
tree | 84fac8f99c678667e01d69d5e2ef17f4c8e8e275 /src/bignum.c | |
parent | 40f8ade7c81ab6f99537691ae00d2d42069bdb20 (diff) | |
download | emacs-fe042e9d15da7863b5beb4c2cc326a62d2c7fccb.tar.gz emacs-fe042e9d15da7863b5beb4c2cc326a62d2c7fccb.tar.bz2 emacs-fe042e9d15da7863b5beb4c2cc326a62d2c7fccb.zip |
Speed up (+ 2 2) by a factor of 10
Improve arithmetic performance by avoiding bignums until needed.
Also, simplify bignum memory management, fixing some unlikely leaks.
This patch improved the performance of (+ 2 2) by a factor of ten
on a simple microbenchmark computing (+ x 2), byte-compiled,
with x a local variable initialized to 2 via means the byte
compiler could not predict: performance improved from 135 to 13 ns.
The platform was Fedora 28 x86-64, AMD Phenom II X4 910e.
Performance also improved 0.6% on ‘make compile-always’.
* src/bignum.c (init_bignum_once): New function.
* src/emacs.c (main): Use it.
* src/bignum.c (mpz): New global var.
(make_integer_mpz): Rename from make_integer. All uses changed.
* src/bignum.c (double_to_bignum, make_bignum_bits)
(make_bignum, make_bigint, make_biguint, make_integer_mpz):
* src/data.c (bignum_arith_driver, Frem, Flogcount, Fash)
(expt_integer, Fadd1, Fsub1, Flognot):
* src/floatfns.c (Fabs, rounding_driver, rounddiv_q):
* src/fns.c (Fnthcdr):
Use mpz rather than mpz_initting and mpz_clearing private
temporaries.
* src/bignum.h (bignum_integer): New function.
* src/data.c (Frem, Fmod, Fash, expt_integer):
* src/floatfns.c (rounding_driver):
Use it to simplify code.
* src/data.c (FIXNUMS_FIT_IN_LONG, free_mpz_value):
Remove. All uses removed.
(floating_point_op): New function.
(floatop_arith_driver): New function, with much of the guts
of the old float_arith_driver.
(float_arith_driver): Use it.
(floatop_arith_driver, arith_driver):
Simplify by assuming NARGS is at least 2.
All callers changed.
(float_arith_driver):
New arg, containing the partly converted value of the next arg.
Reorder args for consistency. All uses changed.
(bignum_arith_driver): New function.
(arith_driver): Use it. Do fixnum-only integer calculations
in intmax_t instead of mpz_t, when they fit.
Break out mpz_t calculations into bignum_arith_driver.
(Fquo): Use floatop_arith_driver instead of float_arith_driver,
since the op is known to be valid.
(Flogcount, Fash): Simplify by coalescing bignum and fixnum code.
(Fadd1, Fsub1): Simplify by using make_int.
Diffstat (limited to 'src/bignum.c')
-rw-r--r-- | src/bignum.c | 71 |
1 files changed, 38 insertions, 33 deletions
diff --git a/src/bignum.c b/src/bignum.c index b18ceccb59d..2ce7412d06c 100644 --- a/src/bignum.c +++ b/src/bignum.c @@ -25,6 +25,22 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <stdlib.h> +/* mpz global temporaries. Making them global saves the trouble of + properly using mpz_init and mpz_clear on temporaries even when + storage is exhausted. Admittedly this is not ideal. An mpz value + in a temporary is made permanent by mpz_swapping it with a bignum's + value. Although typically at most two temporaries are needed, + rounding_driver and rounddiv_q need four altogther. */ + +mpz_t mpz[4]; + +void +init_bignum_once (void) +{ + for (int i = 0; i < ARRAYELTS (mpz); i++) + mpz_init (mpz[i]); +} + /* Return the value of the Lisp bignum N, as a double. */ double bignum_to_double (Lisp_Object n) @@ -36,17 +52,14 @@ bignum_to_double (Lisp_Object n) Lisp_Object double_to_bignum (double d) { - mpz_t z; - mpz_init_set_d (z, d); - Lisp_Object result = make_integer (z); - mpz_clear (z); - return result; + mpz_set_d (mpz[0], d); + return make_integer_mpz (); } -/* Return a Lisp integer equal to OP, which has BITS bits and which - must not be in fixnum range. */ +/* Return a Lisp integer equal to mpz[0], which has BITS bits and which + must not be in fixnum range. Set mpz[0] to a junk value. */ static Lisp_Object -make_bignum_bits (mpz_t const op, size_t bits) +make_bignum_bits (size_t bits) { /* The documentation says integer-width should be nonnegative, so a single comparison suffices even though 'bits' is unsigned. */ @@ -55,18 +68,17 @@ make_bignum_bits (mpz_t const op, size_t bits) struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value, PVEC_BIGNUM); - /* We could mpz_init + mpz_swap here, to avoid a copy, but the - resulting API seemed possibly confusing. */ - mpz_init_set (b->value, op); - + mpz_init (b->value); + mpz_swap (b->value, mpz[0]); return make_lisp_ptr (b, Lisp_Vectorlike); } -/* Return a Lisp integer equal to OP, which must not be in fixnum range. */ +/* Return a Lisp integer equal to mpz[0], which must not be in fixnum range. + Set mpz[0] to a junk value. */ static Lisp_Object -make_bignum (mpz_t const op) +make_bignum (void) { - return make_bignum_bits (op, mpz_sizeinbase (op, 2)); + return make_bignum_bits (mpz_sizeinbase (mpz[0], 2)); } static void mpz_set_uintmax_slow (mpz_t, uintmax_t); @@ -86,30 +98,23 @@ Lisp_Object make_bigint (intmax_t n) { eassert (FIXNUM_OVERFLOW_P (n)); - mpz_t z; - mpz_init (z); - mpz_set_intmax (z, n); - Lisp_Object result = make_bignum (z); - mpz_clear (z); - return result; + mpz_set_intmax (mpz[0], n); + return make_bignum (); } Lisp_Object make_biguint (uintmax_t n) { eassert (FIXNUM_OVERFLOW_P (n)); - mpz_t z; - mpz_init (z); - mpz_set_uintmax (z, n); - Lisp_Object result = make_bignum (z); - mpz_clear (z); - return result; + mpz_set_uintmax (mpz[0], n); + return make_bignum (); } -/* Return a Lisp integer with value taken from OP. */ +/* Return a Lisp integer with value taken from mpz[0]. + Set mpz[0] to a junk value. */ Lisp_Object -make_integer (mpz_t const op) +make_integer_mpz (void) { - size_t bits = mpz_sizeinbase (op, 2); + size_t bits = mpz_sizeinbase (mpz[0], 2); if (bits <= FIXNUM_BITS) { @@ -118,20 +123,20 @@ make_integer (mpz_t const op) do { - EMACS_INT limb = mpz_getlimbn (op, i++); + EMACS_INT limb = mpz_getlimbn (mpz[0], i++); v += limb << shift; shift += GMP_NUMB_BITS; } while (shift < bits); - if (mpz_sgn (op) < 0) + if (mpz_sgn (mpz[0]) < 0) v = -v; if (!FIXNUM_OVERFLOW_P (v)) return make_fixnum (v); } - return make_bignum_bits (op, bits); + return make_bignum_bits (bits); } /* Set RESULT to V. This code is for when intmax_t is wider than long. */ |