From 02dd45c33376f85d1064355dc790dcc4850596b1 Mon Sep 17 00:00:00 2001 From: Olivier Poitrey Date: Sun, 4 Jun 2017 15:45:45 -0700 Subject: [PATCH] Optimize FromString by x100 by unrolling the stdlib algorithm loop --- README.md | 18 +++++++++--------- id.go | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a5cf317..b475c7f 100644 --- a/README.md +++ b/README.md @@ -89,15 +89,15 @@ guid.Counter() Benchmark against Go [Maxim Bublis](https://github.com/satori)'s [UUID](https://github.com/satori/go.uuid). ``` -BenchmarkXID 10000000 138 ns/op 64 B/op 2 allocs/op -BenchmarkXID-2 20000000 96.3 ns/op 64 B/op 2 allocs/op -BenchmarkXID-4 20000000 69.7 ns/op 64 B/op 2 allocs/op -BenchmarkUUIDv1 10000000 207 ns/op 48 B/op 1 allocs/op -BenchmarkUUIDv1-2 10000000 205 ns/op 48 B/op 1 allocs/op -BenchmarkUUIDv1-4 5000000 238 ns/op 48 B/op 1 allocs/op -BenchmarkUUIDv4 1000000 1503 ns/op 64 B/op 2 allocs/op -BenchmarkUUIDv4-2 1000000 1459 ns/op 64 B/op 2 allocs/op -BenchmarkUUIDv4-4 1000000 1468 ns/op 64 B/op 2 allocs/op``` +BenchmarkXID 20000000 91.1 ns/op 32 B/op 1 allocs/op +BenchmarkXID-2 20000000 55.9 ns/op 32 B/op 1 allocs/op +BenchmarkXID-4 50000000 32.3 ns/op 32 B/op 1 allocs/op +BenchmarkUUIDv1 10000000 204 ns/op 48 B/op 1 allocs/op +BenchmarkUUIDv1-2 10000000 160 ns/op 48 B/op 1 allocs/op +BenchmarkUUIDv1-4 10000000 195 ns/op 48 B/op 1 allocs/op +BenchmarkUUIDv4 1000000 1503 ns/op 64 B/op 2 allocs/op +BenchmarkUUIDv4-2 1000000 1427 ns/op 64 B/op 2 allocs/op +BenchmarkUUIDv4-4 1000000 1452 ns/op 64 B/op 2 allocs/op ``` Note: UUIDv1 requires a global lock, hence the performence degrading as we add more CPUs. diff --git a/id.go b/id.go index 36f719f..e76f9ef 100644 --- a/id.go +++ b/id.go @@ -45,7 +45,6 @@ import ( "crypto/md5" "crypto/rand" "database/sql/driver" - "encoding/base32" "encoding/binary" "errors" "fmt" @@ -69,8 +68,6 @@ const ( encoding = "0123456789abcdefghijklmnopqrstuv" ) -var b32enc = base32.NewEncoding(encoding) - // ErrInvalidID is returned when trying to unmarshal an invalid ID var ErrInvalidID = errors.New("xid: invalid ID") @@ -86,6 +83,18 @@ var machineID = readMachineID() // pid stores the current process id var pid = os.Getpid() +// dec is the decoding map for base32 encoding +var dec [256]byte + +func init() { + for i := 0; i < len(dec); i++ { + dec[i] = 0xFF + } + for i := 0; i < len(encoding); i++ { + dec[encoding[i]] = byte(i) + } +} + // readMachineId generates machine id and puts it into the machineId global // variable. If this function fails to get the hostname, it will cause // a runtime error. @@ -154,6 +163,7 @@ func (id ID) MarshalText() ([]byte, error) { return text, nil } +// encode by unrolling the stdlib base32 algorithm + removing all safe checks func encode(dst, id []byte) { dst[0] = encoding[id[0]>>3] dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F] @@ -183,18 +193,28 @@ func (id *ID) UnmarshalText(text []byte) error { return ErrInvalidID } for _, c := range text { - if !(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'v') { + if dec[c] == 0xFF { return ErrInvalidID } } - var ( - bufe [encodedLen + 4]byte - bufd [decodedLen]byte - ) - copy(bufe[:], text) - _, err := b32enc.Decode(bufd[:], append(bufe[:encodedLen], '=', '=', '=', '=')) - copy(id[:], bufd[:]) - return err + decode(id, text) + return nil +} + +// decode by unrolling the stdlib base32 algorithm + removing all safe checks +func decode(id *ID, src []byte) { + id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 + id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 + id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 + id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3 + id[4] = dec[src[6]]<<5 | dec[src[7]] + id[5] = dec[src[8]]<<3 | dec[src[9]]>>2 + id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4 + id[7] = dec[src[11]]<<4 | dec[src[12]]>>1 + id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3 + id[9] = dec[src[14]]<<5 | dec[src[15]] + id[10] = dec[src[16]]<<3 | dec[src[17]]>>2 + id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4 } // Time returns the timestamp part of the id.