]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: speedup nat.setBytes for bigger slices
authorisharipo <iskander.sharipov@intel.com>
Tue, 6 Mar 2018 06:07:34 +0000 (09:07 +0300)
committerRobert Griesemer <gri@golang.org>
Thu, 8 Mar 2018 18:50:10 +0000 (18:50 +0000)
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: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/compile/internal/gc/inl_test.go
src/math/big/nat.go
src/math/big/nat_test.go

index a937c15432da2d672cf9c83b11c329811613acac..d1dc6fbbfa1bb6177c5c7d8ab587683d8aac0f23 100644 (file)
@@ -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" {
index 3bb818f5f259551cf79da28c5d3ab3ee401ec109..c1812e3544573ac577de83b29c2429fd2d4727d0 100644 (file)
@@ -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()
index c25cdf00a3df5f66531e83f44b8ed36380ef4aae..9bb96b11578626c8b079a7f379ff4fd4db21c23a 100644 (file)
@@ -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])
+                       }
+               })
+       }
+}