aboutsummaryrefslogtreecommitdiffstats
path: root/src/bignum.c
diff options
context:
space:
mode:
authorPaul Eggert2018-09-03 18:37:40 -0700
committerPaul Eggert2018-09-03 18:50:34 -0700
commitfe042e9d15da7863b5beb4c2cc326a62d2c7fccb (patch)
tree84fac8f99c678667e01d69d5e2ef17f4c8e8e275 /src/bignum.c
parent40f8ade7c81ab6f99537691ae00d2d42069bdb20 (diff)
downloademacs-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.c71
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
35mpz_t mpz[4];
36
37void
38init_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. */
29double 45double
30bignum_to_double (Lisp_Object n) 46bignum_to_double (Lisp_Object n)
@@ -36,17 +52,14 @@ bignum_to_double (Lisp_Object n)
36Lisp_Object 52Lisp_Object
37double_to_bignum (double d) 53double_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. */
48static Lisp_Object 61static Lisp_Object
49make_bignum_bits (mpz_t const op, size_t bits) 62make_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. */
66static Lisp_Object 78static Lisp_Object
67make_bignum (mpz_t const op) 79make_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
72static void mpz_set_uintmax_slow (mpz_t, uintmax_t); 84static void mpz_set_uintmax_slow (mpz_t, uintmax_t);
@@ -86,30 +98,23 @@ Lisp_Object
86make_bigint (intmax_t n) 98make_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}
96Lisp_Object 104Lisp_Object
97make_biguint (uintmax_t n) 105make_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. */
109Lisp_Object 114Lisp_Object
110make_integer (mpz_t const op) 115make_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. */