simon-git: spigot (master): Simon Tatham
Commits to Tartarus CVS repository.
tartarus-commits at lists.tartarus.org
Sat Apr 16 15:28:49 BST 2016
TL;DR:
9db94bf Fix O(N^2) memory management in internal bigint add/sub.
1aa8cbb Avoid pointless mpz_init_set in GMP bigint operations.
Repository: git://git.tartarus.org/simon/spigot.git
On the web: http://tartarus.org/~simon-git/gitweb/?p=spigot.git
Branch updated: master
Committer: Simon Tatham <anakin at pobox.com>
Date: 2016-04-16 15:28:49
commit 9db94bf8f48a373e267bd110f7531a2bd9f5bbe3
web diff http://tartarus.org/~simon-git/gitweb/?p=spigot.git;a=commitdiff;h=9db94bf8f48a373e267bd110f7531a2bd9f5bbe3;hp=689a6b6988d5ba4b95305de542772a1a9d562fe5
Author: Simon Tatham <anakin at pobox.com>
Date: Sat Apr 16 14:55:51 2016 +0100
Fix O(N^2) memory management in internal bigint add/sub.
I ran across Brendan Gregg's 'flame graph' visualisation of profiling
data recently, and spigot seemed like an easy thing to try it out on.
Running with the internal bigints, I promptly found that an absurd
amount of time was spent in std::vector resize operations called from
bigint::add_word_at_pos(), because I was absentmindedly resizing the
vector one word bigger for each word of the addition, due to the
relevant code being split between two functions so it hadn't jumped
out at me.
Easily fixed by resizing it once before the main addition loop, and
normalising it again afterwards. 'spigot -d5000 pi' with internal
bigints now runs in 13.1 seconds of CPU time (on my machine) compared
to 24.8 before. (Which is still quite bad compared to under half a
second with gmp! But it is just an emergency fallback...)
bi_internal.h | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)
commit 1aa8cbb73f8fcbfd569bb1132054c52f61aeb625
web diff http://tartarus.org/~simon-git/gitweb/?p=spigot.git;a=commitdiff;h=1aa8cbb73f8fcbfd569bb1132054c52f61aeb625;hp=9db94bf8f48a373e267bd110f7531a2bd9f5bbe3
Author: Simon Tatham <anakin at pobox.com>
Date: Sat Apr 16 15:19:26 2016 +0100
Avoid pointless mpz_init_set in GMP bigint operations.
This is another low-hanging-fruit optimisation pointed out by a flame
graph. All bi_gmp.h's non-assigning arithmetic operators (like
operator+) were implemented in terms of the assigning versions like
operator+=, by the idiom 'bigint ret = a; return ret += b;'.
Unfortunately, the construction of ret unpacks to an mpz_init_set that
copies a, followed by an addition of b, and it turns out that the copy
takes about as long as the addition, so it's faster to just initialise
the output bigint as empty and overwrite it directly with the sum of a
and b.
Fixed by abstracting the actual gmp calls out into subroutines, so
that now instead of operator+ being based on operator+=, both are
based on overwrite_add(), and similarly for all the other operations
I'd implemented this way. (The exception is reversed subtraction of
the form 'ordinary int minus bigint', because there's no gmp call that
does that in one go for a signed integer, so you do need a separate
mpz_neg.)
Now my 5000-digits pi test *with* gmp has gone down from 0.444s to
0.299s, which is nice.
bi_gmp.h | 73 +++++++++++++++++++++++++++++++++++++-------------------------
1 file changed, 44 insertions(+), 29 deletions(-)
More information about the tartarus-commits
mailing list