-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Closed
Closed
Copy link
Labels
Milestone
Description
Currently big.Float.Add and big.Float.Sub allocate even if none of the arguments alias the receiver. Code:
package benchAddSub
import (
"math/big"
"testing"
)
var prec uint = 1e5
var x *big.Float = new(big.Float).SetPrec(prec).SetUint64(2)
var y *big.Float = new(big.Float).SetPrec(prec)
var t *big.Float = new(big.Float).SetPrec(prec)
func init() {
y.Parse("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605714701095599716059702745345968620147285174186408891986095523292304843087143214508397626036279952514079896872533965463318088296406206152583523950547457502877599617298355752203375318570113543746034084988471603868999706990048150305440277903164542478230684929369186215805784631115966687130130156185689872372352885092648612494977154218334204285686060146824720771435854874155657069677653720226485447015858801620758474922657226002085584466521458398893944370926591800311388246468157082630100594858704003186480342194897278290641045072636881313739855256117322040245091227700226941127573627280495738108967504018369868368450725799364729060762996941380475654823728997180326802474420629269124859052181004459842150591120249441341728531478105803603371077309182869314710171111683916581726889419758716582152128229518488472089694633862891562882765952635140542", 10)
}
func BenchmarkAddFloats(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
t.Add(x, y)
}
}
func BenchmarkSubFloats(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
t.Sub(x, y)
}
}(playground http://play.golang.org/p/rNaj0NwPOT) gives
BenchmarkAddFloats-4 200000 6124 ns/op 13568 B/op 1 allocs/op
BenchmarkSubFloats-4 300000 5732 ns/op 13568 B/op 1 allocs/op
The relevant code (for Add) is here: https://github.com/golang/go/blob/master/src/math/big/float.go#L1229-L1242
Looks like it shouldn't be hard to fix, maybe something like
al := alias(z.mant, x.mant) || alias(z.mant, y.mant)
switch {
case ex < ey:
if !al { // no aliasing, can re-use z
z.mant = z.mant.shl(y.mant, uint(ey-ex))
z.mant = z.mant.sub(x.mant, z.mant)
} else {
t := nat(nil).shl(y.mant, uint(ey-ex))
z.mant = t.sub(x.mant, t)
}
...