base58: allocate less in Encode

* calculate maximum output length more precisely
  to avoid allocation in the append
* use big.Int.Sign instead of needing bigZero

name                 old time/op    new time/op    delta
Base58Encode_5K-8      5.86ms ± 3%    5.79ms ± 2%   -1.27%  (p=0.035 n=9+10)
Base58Encode_100K-8     2.23s ± 1%     2.23s ± 0%     ~     (p=0.074 n=9+8)
Base58Decode_5K-8       281µs ± 1%     282µs ± 1%     ~     (p=0.720 n=9+10)
Base58Decode_100K-8    89.4ms ± 7%    88.3ms ± 7%     ~     (p=0.123 n=10+10)

name                 old speed      new speed      delta
Base58Encode_5K-8     854kB/s ± 3%   864kB/s ± 2%     ~     (p=0.134 n=9+10)
Base58Encode_100K-8  40.0kB/s ± 0%  40.0kB/s ± 0%     ~     (all equal)
Base58Decode_5K-8    24.3MB/s ± 1%  24.2MB/s ± 1%     ~     (p=0.644 n=9+10)
Base58Decode_100K-8  1.53MB/s ± 7%  1.55MB/s ± 7%     ~     (p=0.218 n=10+10)

name                 old alloc/op   new alloc/op   delta
Base58Encode_5K-8      28.7kB ± 0%    19.2kB ± 0%  -33.03%  (p=0.000 n=10+10)
Base58Encode_100K-8     557kB ± 0%     385kB ± 0%  -30.88%  (p=0.000 n=10+10)
Base58Decode_5K-8       349kB ± 0%     349kB ± 0%     ~     (all equal)
Base58Decode_100K-8     133MB ± 0%     133MB ± 0%     ~     (p=0.183 n=10+10)

name                 old allocs/op  new allocs/op  delta
Base58Encode_5K-8        5.00 ± 0%      4.00 ± 0%  -20.00%  (p=0.000 n=10+10)
Base58Encode_100K-8      5.00 ± 0%      4.00 ± 0%  -20.00%  (p=0.000 n=10+10)
Base58Decode_5K-8         129 ± 0%       129 ± 0%     ~     (all equal)
Base58Decode_100K-8     2.51k ± 0%     2.51k ± 0%     ~     (p=0.321 n=10+10)

When Go 1.16 is released, performance will improve
significantly due to improvements to math/big.Int's division implementation.
This commit is contained in:
Josh Bleecher Snyder 2020-09-25 16:27:43 -07:00 committed by John C. Vernaleo
parent d63d9f2b44
commit a53e38424c

View file

@ -25,7 +25,6 @@ var bigRadix = [...]*big.Int{
} }
var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10 var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
var bigZero = big.NewInt(0)
// Decode decodes a modified base58 string to a byte slice. // Decode decodes a modified base58 string to a byte slice.
func Decode(b string) []byte { func Decode(b string) []byte {
@ -90,9 +89,11 @@ func Encode(b []byte) string {
x := new(big.Int) x := new(big.Int)
x.SetBytes(b) x.SetBytes(b)
answer := make([]byte, 0, len(b)*136/100) // maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
maxlen := int(float64(len(b))*1.365658237309761) + 1
answer := make([]byte, 0, maxlen)
mod := new(big.Int) mod := new(big.Int)
for x.Cmp(bigZero) > 0 { for x.Sign() > 0 {
// Calculating with big.Int is slow for each iteration. // Calculating with big.Int is slow for each iteration.
// x, mod = x / 58, x % 58 // x, mod = x / 58, x % 58
// //
@ -103,7 +104,7 @@ func Encode(b []byte) string {
// We'll loop that 10 times to convert to the answer. // We'll loop that 10 times to convert to the answer.
x.DivMod(x, bigRadix10, mod) x.DivMod(x, bigRadix10, mod)
if x.Cmp(bigZero) == 0 { if x.Sign() == 0 {
// When x = 0, we need to ensure we don't add any extra zeros. // When x = 0, we need to ensure we don't add any extra zeros.
m := mod.Int64() m := mod.Int64()
for m > 0 { for m > 0 {