Commit d2a5263a authored by isharipo's avatar isharipo Committed by Robert Griesemer

math/big: speedup nat.setBytes for bigger slices

Set up to _S (number of bytes in Uint) bytes at time
by using BigEndian.Uint32 and BigEndian.Uint64.

The performance improves for slices bigger than _S bytes.
This is the case for 128/256bit arith that initializes
it's objects from bytes.

name               old time/op  new time/op  delta
NatSetBytes/8-4    29.8ns ± 1%  11.4ns ± 0%  -61.63%  (p=0.000 n=9+8)
NatSetBytes/24-4    109ns ± 1%    56ns ± 0%  -48.75%  (p=0.000 n=9+8)
NatSetBytes/128-4   420ns ± 2%   110ns ± 1%  -73.83%  (p=0.000 n=10+10)
NatSetBytes/7-4    26.2ns ± 1%  21.3ns ± 2%  -18.63%  (p=0.000 n=8+9)
NatSetBytes/23-4    106ns ± 1%    67ns ± 1%  -36.93%  (p=0.000 n=9+10)
NatSetBytes/127-4   410ns ± 2%   121ns ± 0%  -70.46%  (p=0.000 n=9+8)

Found this optimization opportunity by looking at ethereum_corevm
community benchmark cpuprofile.

name        old time/op  new time/op  delta
OpDiv256-4   715ns ± 1%   596ns ± 1%  -16.57%  (p=0.008 n=5+5)
OpDiv128-4   373ns ± 1%   314ns ± 1%  -15.83%  (p=0.008 n=5+5)
OpDiv64-4    301ns ± 0%   285ns ± 1%   -5.12%  (p=0.008 n=5+5)

Change-Id: I8e5a680ae6284c8233d8d7431d51253a8a740b57
Reviewed-on: https://go-review.googlesource.com/98775
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent dcac984b
......@@ -133,6 +133,9 @@ func TestIntendedInlining(t *testing.T) {
"regexp": {
"(*bitState).push",
},
"math/big": {
"bigEndianWord",
},
}
if runtime.GOARCH != "386" && runtime.GOARCH != "mips64" && runtime.GOARCH != "mips64le" {
......
......@@ -9,6 +9,7 @@
package big
import (
"encoding/binary"
"math/bits"
"math/rand"
"sync"
......@@ -1208,25 +1209,32 @@ func (z nat) bytes(buf []byte) (i int) {
return
}
// bigEndianWord returns the contents of buf interpreted as a big-endian encoded Word value.
func bigEndianWord(buf []byte) Word {
if _W == 64 {
return Word(binary.BigEndian.Uint64(buf))
} else { // Explicit else is required to get inlining. See #23521
return Word(binary.BigEndian.Uint32(buf))
}
}
// setBytes interprets buf as the bytes of a big-endian unsigned
// integer, sets z to that value, and returns z.
func (z nat) setBytes(buf []byte) nat {
z = z.make((len(buf) + _S - 1) / _S)
k := 0
s := uint(0)
var d Word
for i := len(buf); i > 0; i-- {
d |= Word(buf[i-1]) << s
if s += 8; s == _S*8 {
z[k] = d
k++
s = 0
d = 0
}
i := len(buf)
for k := 0; i >= _S; k++ {
z[k] = bigEndianWord(buf[i-_S : i])
i -= _S
}
if k < len(z) {
z[k] = d
if i > 0 {
var d Word
for s := uint(0); i > 0; s += 8 {
d |= Word(buf[i-1]) << s
i--
}
z[len(z)-1] = d
}
return z.norm()
......
......@@ -665,3 +665,22 @@ func BenchmarkNatSqr(b *testing.B) {
})
}
}
func BenchmarkNatSetBytes(b *testing.B) {
const maxLength = 128
lengths := []int{
// No remainder:
8, 24, maxLength,
// With remainder:
7, 23, maxLength - 1,
}
n := make(nat, maxLength/_W) // ensure n doesn't need to grow during the test
buf := make([]byte, maxLength)
for _, l := range lengths {
b.Run(fmt.Sprint(l), func(b *testing.B) {
for i := 0; i < b.N; i++ {
n.setBytes(buf[:l])
}
})
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment