From a53e38424cce1c9de2062b69364efd35fd428d15 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 25 Sep 2020 16:27:43 -0700 Subject: [PATCH] base58: allocate less in Encode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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. --- base58/base58.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/base58/base58.go b/base58/base58.go index f6c4b71..8ee5956 100644 --- a/base58/base58.go +++ b/base58/base58.go @@ -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 bigZero = big.NewInt(0) // Decode decodes a modified base58 string to a byte slice. func Decode(b string) []byte { @@ -90,9 +89,11 @@ func Encode(b []byte) string { x := new(big.Int) 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) - for x.Cmp(bigZero) > 0 { + for x.Sign() > 0 { // Calculating with big.Int is slow for each iteration. // 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. 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. m := mod.Int64() for m > 0 {