Commit 2fbfe55e authored by Rui Ueyama's avatar Rui Ueyama

encoding/base64, encoding/base32: make Encode faster

Storing temporary values to a slice is slower than storing
them to local variables of type byte.

benchmark                         old MB/s     new MB/s  speedup
BenchmarkEncodeToStringBase32       102.21       156.66    1.53x
BenchmarkEncodeToStringBase64       124.25       177.91    1.43x

LGTM=crawshaw
R=golang-codereviews, crawshaw, bradfitz, dave
CC=golang-codereviews
https://golang.org/cl/109820045
parent 0e92b538
...@@ -73,45 +73,43 @@ func (enc *Encoding) Encode(dst, src []byte) { ...@@ -73,45 +73,43 @@ func (enc *Encoding) Encode(dst, src []byte) {
} }
for len(src) > 0 { for len(src) > 0 {
dst[0] = 0 var b0, b1, b2, b3, b4, b5, b6, b7 byte
dst[1] = 0
dst[2] = 0
dst[3] = 0
dst[4] = 0
dst[5] = 0
dst[6] = 0
dst[7] = 0
// Unpack 8x 5-bit source blocks into a 5 byte // Unpack 8x 5-bit source blocks into a 5 byte
// destination quantum // destination quantum
switch len(src) { switch len(src) {
default: default:
dst[7] |= src[4] & 0x1F b7 |= src[4] & 0x1F
dst[6] |= src[4] >> 5 b6 |= src[4] >> 5
fallthrough fallthrough
case 4: case 4:
dst[6] |= (src[3] << 3) & 0x1F b6 |= (src[3] << 3) & 0x1F
dst[5] |= (src[3] >> 2) & 0x1F b5 |= (src[3] >> 2) & 0x1F
dst[4] |= src[3] >> 7 b4 |= src[3] >> 7
fallthrough fallthrough
case 3: case 3:
dst[4] |= (src[2] << 1) & 0x1F b4 |= (src[2] << 1) & 0x1F
dst[3] |= (src[2] >> 4) & 0x1F b3 |= (src[2] >> 4) & 0x1F
fallthrough fallthrough
case 2: case 2:
dst[3] |= (src[1] << 4) & 0x1F b3 |= (src[1] << 4) & 0x1F
dst[2] |= (src[1] >> 1) & 0x1F b2 |= (src[1] >> 1) & 0x1F
dst[1] |= (src[1] >> 6) & 0x1F b1 |= (src[1] >> 6) & 0x1F
fallthrough fallthrough
case 1: case 1:
dst[1] |= (src[0] << 2) & 0x1F b1 |= (src[0] << 2) & 0x1F
dst[0] |= src[0] >> 3 b0 |= src[0] >> 3
} }
// Encode 5-bit blocks using the base32 alphabet // Encode 5-bit blocks using the base32 alphabet
for j := 0; j < 8; j++ { dst[0] = enc.encode[b0]
dst[j] = enc.encode[dst[j]] dst[1] = enc.encode[b1]
} dst[2] = enc.encode[b2]
dst[3] = enc.encode[b3]
dst[4] = enc.encode[b4]
dst[5] = enc.encode[b5]
dst[6] = enc.encode[b6]
dst[7] = enc.encode[b7]
// Pad the final quantum // Pad the final quantum
if len(src) < 5 { if len(src) < 5 {
......
...@@ -284,3 +284,19 @@ LNEBUWIIDFON2CA3DBMJXXE5LNFY== ...@@ -284,3 +284,19 @@ LNEBUWIIDFON2CA3DBMJXXE5LNFY==
t.Error("Decoded results not equal") t.Error("Decoded results not equal")
} }
} }
func BenchmarkEncodeToString(b *testing.B) {
data := make([]byte, 8192)
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
StdEncoding.EncodeToString(data)
}
}
func BenchmarkDecodeString(b *testing.B) {
data := StdEncoding.EncodeToString(make([]byte, 8192))
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
StdEncoding.DecodeString(data)
}
}
...@@ -74,31 +74,29 @@ func (enc *Encoding) Encode(dst, src []byte) { ...@@ -74,31 +74,29 @@ func (enc *Encoding) Encode(dst, src []byte) {
} }
for len(src) > 0 { for len(src) > 0 {
dst[0] = 0 var b0, b1, b2, b3 byte
dst[1] = 0
dst[2] = 0
dst[3] = 0
// Unpack 4x 6-bit source blocks into a 4 byte // Unpack 4x 6-bit source blocks into a 4 byte
// destination quantum // destination quantum
switch len(src) { switch len(src) {
default: default:
dst[3] |= src[2] & 0x3F b3 |= src[2] & 0x3F
dst[2] |= src[2] >> 6 b2 |= src[2] >> 6
fallthrough fallthrough
case 2: case 2:
dst[2] |= (src[1] << 2) & 0x3F b2 |= (src[1] << 2) & 0x3F
dst[1] |= src[1] >> 4 b1 |= src[1] >> 4
fallthrough fallthrough
case 1: case 1:
dst[1] |= (src[0] << 4) & 0x3F b1 |= (src[0] << 4) & 0x3F
dst[0] |= src[0] >> 2 b0 |= src[0] >> 2
} }
// Encode 6-bit blocks using the base64 alphabet // Encode 6-bit blocks using the base64 alphabet
for j := 0; j < 4; j++ { dst[0] = enc.encode[b0]
dst[j] = enc.encode[dst[j]] dst[1] = enc.encode[b1]
} dst[2] = enc.encode[b2]
dst[3] = enc.encode[b3]
// Pad the final quantum // Pad the final quantum
if len(src) < 3 { if len(src) < 3 {
......
...@@ -343,6 +343,14 @@ func TestDecoderIssue7733(t *testing.T) { ...@@ -343,6 +343,14 @@ func TestDecoderIssue7733(t *testing.T) {
} }
} }
func BenchmarkEncodeToString(b *testing.B) {
data := make([]byte, 8192)
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
StdEncoding.EncodeToString(data)
}
}
func BenchmarkDecodeString(b *testing.B) { func BenchmarkDecodeString(b *testing.B) {
data := StdEncoding.EncodeToString(make([]byte, 8192)) data := StdEncoding.EncodeToString(make([]byte, 8192))
b.SetBytes(int64(len(data))) b.SetBytes(int64(len(data)))
......
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