Skip to content

math/big: Float.{Add, Sub} allocate even when receiver does not alias the arguments #14868

@ALTree

Description

@ALTree

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)
    }
...

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions