Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

math/big: recognize z.Mul(x, x) as squaring of x #13745

Closed
griesemer opened this issue Dec 27, 2015 · 8 comments
Closed

math/big: recognize z.Mul(x, x) as squaring of x #13745

griesemer opened this issue Dec 27, 2015 · 8 comments

Comments

@griesemer
Copy link
Contributor

Squaring x*x of a number x can be implemented more efficiently than general multiplication x*y. Instead of providing an additional Sqr function, recognize calls of the form z.Mul(x, x) and internally use squaring code. This will boost squaring code independent of whether an explicit square function was used or not.

@griesemer griesemer self-assigned this Dec 27, 2015
@griesemer griesemer added this to the Unplanned milestone Dec 27, 2015
@odeke-em
Copy link
Member

Am excited to see how this will be implemented as I haven't found the internal squaring code. If am making too much noise on this thread, please let me know and I'll email you.

@gopherbot
Copy link

Change https://golang.org/cl/53638 mentions this issue: math/big: recognize z.Mul(x, x) as squaring of x

@bmkessler
Copy link
Contributor

I had just had a quick go at getting this started with the above CL. I added a routine to the nat multiplication that does the schoolbook squaring with about half the multiplications, but additional bookkeeping overhead to collect, shift and add the cross products. There is some improvement for intermediate range (I'm sure the basic squaring routine I wrote could be optimized) before karatsuba multiplication is better, but there is also a hit because of the costly equality check on the arguments. I chose to put the code in nat directly so that all downstream calls would benefit, but an alternative is to push the check to recognize squaring to the types embedding nats (Int, Rat, Float) where it would just be a pointer comparison at very small cost with the necessary complication of catching and checking each instance of mul for nats.

@griesemer
Copy link
Contributor Author

@bmkessler Thanks. Please let's do the latter: introduce a new method nat.sqr and then use a pointer comparison in Int.Mul/Rat/Float as needed. Start with Int for now (separate CLs for the others). It's a bit more code but it will not make nat.mul slower in the general case and it will make squaring faster because many additional checks can be simplified or avoided. See also my comments on the CL.

gopherbot pushed a commit that referenced this issue Aug 16, 2017
updates #13745

Multiprecision squaring can be done in a straightforward manner
with about half the multiplications of a basic multiplication
due to the symmetry of the operands.  This change implements
basic squaring for nat types and uses it for Int multiplication
when the same variable is supplied to both arguments of
z.Mul(x, x). This has some overhead to allocate a temporary
variable to hold the cross products, shift them to double and
add them to the diagonal terms.  There is a speed benefit in
the intermediate range when the overhead is neglible and the
asymptotic performance of karatsuba multiplication has not been
reached.

basicSqrThreshold = 20
karatsubaSqrThreshold = 400

Were set by running calibrate_test.go to measure timing differences
between the algorithms.  Benchmarks for squaring:

name           old time/op  new time/op  delta
IntSqr/1-4     51.5ns ±25%  25.1ns ± 7%  -51.38%  (p=0.008 n=5+5)
IntSqr/2-4     79.1ns ± 4%  72.4ns ± 2%   -8.47%  (p=0.008 n=5+5)
IntSqr/3-4      102ns ± 4%    97ns ± 5%     ~     (p=0.056 n=5+5)
IntSqr/5-4      161ns ± 4%   163ns ± 7%     ~     (p=0.952 n=5+5)
IntSqr/8-4      277ns ± 5%   267ns ± 6%     ~     (p=0.087 n=5+5)
IntSqr/10-4     358ns ± 3%   360ns ± 4%     ~     (p=0.730 n=5+5)
IntSqr/20-4    1.07µs ± 3%  1.01µs ± 6%     ~     (p=0.056 n=5+5)
IntSqr/30-4    2.36µs ± 4%  1.72µs ± 2%  -27.03%  (p=0.008 n=5+5)
IntSqr/50-4    5.19µs ± 3%  3.88µs ± 4%  -25.37%  (p=0.008 n=5+5)
IntSqr/80-4    11.3µs ± 4%   8.6µs ± 3%  -23.78%  (p=0.008 n=5+5)
IntSqr/100-4   16.2µs ± 4%  12.8µs ± 3%  -21.49%  (p=0.008 n=5+5)
IntSqr/200-4   50.1µs ± 5%  44.7µs ± 3%  -10.65%  (p=0.008 n=5+5)
IntSqr/300-4    105µs ±11%    95µs ± 3%   -9.50%  (p=0.008 n=5+5)
IntSqr/500-4    231µs ± 5%   227µs ± 2%     ~     (p=0.310 n=5+5)
IntSqr/800-4    496µs ± 9%   459µs ± 3%   -7.40%  (p=0.016 n=5+5)
IntSqr/1000-4   700µs ± 3%   710µs ± 5%     ~     (p=0.841 n=5+5)

Show a speed up of 10-25% in the range where basicSqr is optimal,
improved single word squaring and no significant difference when
the fallback to standard multiplication is used.

Change-Id: Iae2c82ca91cf890823f91e5c83bbe9a2c534b72b
Reviewed-on: https://go-review.googlesource.com/53638
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@gopherbot
Copy link

Change https://golang.org/cl/56774 mentions this issue: math/big: recognize squaring for Floats

@gopherbot
Copy link

Change https://golang.org/cl/56776 mentions this issue: math/big: use internal square for Rat

gopherbot pushed a commit that referenced this issue Aug 18, 2017
updates #13745

A squared rational is always positive and can not
be reduced since the numerator and denominator had
no previous common factors.  The nat multiplication
can be performed using the internal sqr method.

Change-Id: I558f5b38e379bfd26ff163c9489006d7e5a9cfaa
Reviewed-on: https://go-review.googlesource.com/56776
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
gopherbot pushed a commit that referenced this issue Aug 18, 2017
Updates #13745

Recognize z.Mul(x, x) as squaring for Floats and use
the internal z.sqr(x) method for nat on the mantissa.

Change-Id: I0f792157bad93a13cae1aecc4c10bd20c6397693
Reviewed-on: https://go-review.googlesource.com/56774
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@odeke-em
Copy link
Member

Gentle ping here, @griesemer what's left to do for this issue?

I ask because it seems like all the exported arithmetic types seem to have this recognition in as tabulated below:

Type Done CL Code
Float Yes https://go-review.googlesource.com/c/go/+/56774

go/src/math/big/float.go

Lines 1314 to 1318 in 60be6f8

if x == y {
z.mant = z.mant.sqr(x.mant)
} else {
z.mant = z.mant.mul(x.mant, y.mant)
}
Rat Yes https://go-review.googlesource.com/#/c/56776/

go/src/math/big/rat.go

Lines 493 to 498 in 60be6f8

if x == y {
// a squared Rat is positive and can't be reduced
z.a.neg = false
z.a.abs = z.a.abs.sqr(x.a.abs)
z.b.abs = z.b.abs.sqr(x.b.abs)
return z
Int Yes https://go-review.googlesource.com/53638

go/src/math/big/int.go

Lines 156 to 160 in 60be6f8

if x == y {
z.abs = z.abs.sqr(x.abs)
z.neg = false
return z
}

@griesemer
Copy link
Contributor Author

Thanks, @odeke-em, for looking into this. I believe Karatsuba multiplication can also be made faster if we know that we are squaring, but that's arguably a lower-level issue. I will file another issue for that and close this.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants