diff options
| author | Paul Eggert | 2018-09-03 18:37:40 -0700 |
|---|---|---|
| committer | Paul Eggert | 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.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/>. */ | |||
| 25 | 25 | ||
| 26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
| 27 | 27 | ||
| 28 | /* mpz global temporaries. Making them global saves the trouble of | ||
| 29 | properly using mpz_init and mpz_clear on temporaries even when | ||
| 30 | storage is exhausted. Admittedly this is not ideal. An mpz value | ||
| 31 | in a temporary is made permanent by mpz_swapping it with a bignum's | ||
| 32 | value. Although typically at most two temporaries are needed, | ||
| 33 | rounding_driver and rounddiv_q need four altogther. */ | ||
| 34 | |||
| 35 | mpz_t mpz[4]; | ||
| 36 | |||
| 37 | void | ||
| 38 | init_bignum_once (void) | ||
| 39 | { | ||
| 40 | for (int i = 0; i < ARRAYELTS (mpz); i++) | ||
| 41 | mpz_init (mpz[i]); | ||
| 42 | } | ||
| 43 | |||
| 28 | /* Return the value of the Lisp bignum N, as a double. */ | 44 | /* Return the value of the Lisp bignum N, as a double. */ |
| 29 | double | 45 | double |
| 30 | bignum_to_double (Lisp_Object n) | 46 | bignum_to_double (Lisp_Object n) |
| @@ -36,17 +52,14 @@ bignum_to_double (Lisp_Object n) | |||
| 36 | Lisp_Object | 52 | Lisp_Object |
| 37 | double_to_bignum (double d) | 53 | double_to_bignum (double d) |
| 38 | { | 54 | { |
| 39 | mpz_t z; | 55 | mpz_set_d (mpz[0], d); |
| 40 | mpz_init_set_d (z, d); | 56 | return make_integer_mpz (); |
| 41 | Lisp_Object result = make_integer (z); | ||
| 42 | mpz_clear (z); | ||
| 43 | return result; | ||
| 44 | } | 57 | } |
| 45 | 58 | ||
| 46 | /* Return a Lisp integer equal to OP, which has BITS bits and which | 59 | /* Return a Lisp integer equal to mpz[0], which has BITS bits and which |
| 47 | must not be in fixnum range. */ | 60 | must not be in fixnum range. Set mpz[0] to a junk value. */ |
| 48 | static Lisp_Object | 61 | static Lisp_Object |
| 49 | make_bignum_bits (mpz_t const op, size_t bits) | 62 | make_bignum_bits (size_t bits) |
| 50 | { | 63 | { |
| 51 | /* The documentation says integer-width should be nonnegative, so | 64 | /* The documentation says integer-width should be nonnegative, so |
| 52 | a single comparison suffices even though 'bits' is unsigned. */ | 65 | a single comparison suffices even though 'bits' is unsigned. */ |
| @@ -55,18 +68,17 @@ make_bignum_bits (mpz_t const op, size_t bits) | |||
| 55 | 68 | ||
| 56 | struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value, | 69 | struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value, |
| 57 | PVEC_BIGNUM); | 70 | PVEC_BIGNUM); |
| 58 | /* We could mpz_init + mpz_swap here, to avoid a copy, but the | 71 | mpz_init (b->value); |
| 59 | resulting API seemed possibly confusing. */ | 72 | mpz_swap (b->value, mpz[0]); |
| 60 | mpz_init_set (b->value, op); | ||
| 61 | |||
| 62 | return make_lisp_ptr (b, Lisp_Vectorlike); | 73 | return make_lisp_ptr (b, Lisp_Vectorlike); |
| 63 | } | 74 | } |
| 64 | 75 | ||
| 65 | /* Return a Lisp integer equal to OP, which must not be in fixnum range. */ | 76 | /* Return a Lisp integer equal to mpz[0], which must not be in fixnum range. |
| 77 | Set mpz[0] to a junk value. */ | ||
| 66 | static Lisp_Object | 78 | static Lisp_Object |
| 67 | make_bignum (mpz_t const op) | 79 | make_bignum (void) |
| 68 | { | 80 | { |
| 69 | return make_bignum_bits (op, mpz_sizeinbase (op, 2)); | 81 | return make_bignum_bits (mpz_sizeinbase (mpz[0], 2)); |
| 70 | } | 82 | } |
| 71 | 83 | ||
| 72 | static void mpz_set_uintmax_slow (mpz_t, uintmax_t); | 84 | static void mpz_set_uintmax_slow (mpz_t, uintmax_t); |
| @@ -86,30 +98,23 @@ Lisp_Object | |||
| 86 | make_bigint (intmax_t n) | 98 | make_bigint (intmax_t n) |
| 87 | { | 99 | { |
| 88 | eassert (FIXNUM_OVERFLOW_P (n)); | 100 | eassert (FIXNUM_OVERFLOW_P (n)); |
| 89 | mpz_t z; | 101 | mpz_set_intmax (mpz[0], n); |
| 90 | mpz_init (z); | 102 | return make_bignum (); |
| 91 | mpz_set_intmax (z, n); | ||
| 92 | Lisp_Object result = make_bignum (z); | ||
| 93 | mpz_clear (z); | ||
| 94 | return result; | ||
| 95 | } | 103 | } |
| 96 | Lisp_Object | 104 | Lisp_Object |
| 97 | make_biguint (uintmax_t n) | 105 | make_biguint (uintmax_t n) |
| 98 | { | 106 | { |
| 99 | eassert (FIXNUM_OVERFLOW_P (n)); | 107 | eassert (FIXNUM_OVERFLOW_P (n)); |
| 100 | mpz_t z; | 108 | mpz_set_uintmax (mpz[0], n); |
| 101 | mpz_init (z); | 109 | return make_bignum (); |
| 102 | mpz_set_uintmax (z, n); | ||
| 103 | Lisp_Object result = make_bignum (z); | ||
| 104 | mpz_clear (z); | ||
| 105 | return result; | ||
| 106 | } | 110 | } |
| 107 | 111 | ||
| 108 | /* Return a Lisp integer with value taken from OP. */ | 112 | /* Return a Lisp integer with value taken from mpz[0]. |
| 113 | Set mpz[0] to a junk value. */ | ||
| 109 | Lisp_Object | 114 | Lisp_Object |
| 110 | make_integer (mpz_t const op) | 115 | make_integer_mpz (void) |
| 111 | { | 116 | { |
| 112 | size_t bits = mpz_sizeinbase (op, 2); | 117 | size_t bits = mpz_sizeinbase (mpz[0], 2); |
| 113 | 118 | ||
| 114 | if (bits <= FIXNUM_BITS) | 119 | if (bits <= FIXNUM_BITS) |
| 115 | { | 120 | { |
| @@ -118,20 +123,20 @@ make_integer (mpz_t const op) | |||
| 118 | 123 | ||
| 119 | do | 124 | do |
| 120 | { | 125 | { |
| 121 | EMACS_INT limb = mpz_getlimbn (op, i++); | 126 | EMACS_INT limb = mpz_getlimbn (mpz[0], i++); |
| 122 | v += limb << shift; | 127 | v += limb << shift; |
| 123 | shift += GMP_NUMB_BITS; | 128 | shift += GMP_NUMB_BITS; |
| 124 | } | 129 | } |
| 125 | while (shift < bits); | 130 | while (shift < bits); |
| 126 | 131 | ||
| 127 | if (mpz_sgn (op) < 0) | 132 | if (mpz_sgn (mpz[0]) < 0) |
| 128 | v = -v; | 133 | v = -v; |
| 129 | 134 | ||
| 130 | if (!FIXNUM_OVERFLOW_P (v)) | 135 | if (!FIXNUM_OVERFLOW_P (v)) |
| 131 | return make_fixnum (v); | 136 | return make_fixnum (v); |
| 132 | } | 137 | } |
| 133 | 138 | ||
| 134 | return make_bignum_bits (op, bits); | 139 | return make_bignum_bits (bits); |
| 135 | } | 140 | } |
| 136 | 141 | ||
| 137 | /* Set RESULT to V. This code is for when intmax_t is wider than long. */ | 142 | /* Set RESULT to V. This code is for when intmax_t is wider than long. */ |