Skip to content

Commit

Permalink
Optimize FromString by x100 by unrolling the stdlib algorithm loop
Browse files Browse the repository at this point in the history
  • Loading branch information
rs committed Jun 4, 2017
1 parent 9356791 commit 02dd45c
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 21 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
44 changes: 32 additions & 12 deletions id.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import (
"crypto/md5"
"crypto/rand"
"database/sql/driver"
"encoding/base32"
"encoding/binary"
"errors"
"fmt"
Expand All @@ -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")

Expand All @@ -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.
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 02dd45c

Please sign in to comment.