Skip to content

Instantly share code, notes, and snippets.

@evalphobia
Last active December 19, 2022 04:15
Show Gist options
  • Save evalphobia/1f40afcfc73ce207d890dd4b1705a0c9 to your computer and use it in GitHub Desktop.
Save evalphobia/1f40afcfc73ce207d890dd4b1705a0c9 to your computer and use it in GitHub Desktop.
Golang Benchmark: uuid(v4) vs cuid vs nanoid

Golang Benchmark: uuid(v4) vs cuid vs nanoid

Comparing these libraries

  • github.com/google/uuid
  • github.com/lucsky/cuid
  • github.com/matoous/go-nanoid

result

$ go version
go version go1.16 darwin/arm64

$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Apple M1

$ go test -bench . -benchmem -benchtime 4s ./
goos: darwin
goarch: arm64
pkg: github.com/evalphobia/benchmark-uuid
BenchmarkUUID-8            	43347363	       108.5 ns/op	      64 B/op	       2 allocs/op
BenchmarkCUIDNew-8         	22307738	       212.0 ns/op	      55 B/op	       4 allocs/op
BenchmarkCUIDSlug-8        	25321903	       198.3 ns/op	      32 B/op	       3 allocs/op
BenchmarkCUIDNewCrypto-8   	10721586	       455.2 ns/op	     168 B/op	      12 allocs/op
BenchmarkNanoID-8          	19495198	       238.7 ns/op	     144 B/op	       3 allocs/op
BenchmarkHashids-8         	10606057	       460.7 ns/op	     512 B/op	       5 allocs/op
BenchmarkSHA256-8          	24044833	       202.2 ns/op	     120 B/op	       3 allocs/op
PASS
ok  	github.com/evalphobia/benchmark-uuid	57.397s

collision test

Size: 100M ids

 go test -v -count 1 -timeout 1h
=== RUN   TestCollisionUUID
    collision_test.go:23: [uuid.NewString()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [uuid.NewString()] Example output => []string{"23a16872-5718-4de6-928a-64374df60096", "92333847-e968-490a-bd17-a4041f3ad5b8", "68a4ff5f-572a-4318-bdcc-bcd84479c5a2", "4a35d4e2-3f7f-430d-8c0b-ee2bec1e2e14", "fa40d5e7-ab0d-40fc-9837-22df6bfb22f0"}
--- PASS: TestCollisionUUID (77.34s)
=== RUN   TestCollisionCUIDNew
    collision_test.go:23: [cuid.New()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [cuid.New()] Example output => []string{"ckosap7d1g9fn85962zsgiw2l", "ckosapg2e66hr85965u9il26t", "ckosaplnuhgfa8596rspq9tip", "ckosapn4q5az68596idtfl3e0", "ckosapw2re4vt8596f3jr0fjq"}
--- PASS: TestCollisionCUIDNew (84.97s)
=== RUN   TestCollisionCUIDSlug
    collision_test.go:23: [cuid.Slug()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [cuid.Slug()] Example output => []string{"jzaug86zlbu", "pm81lg864i8f", "ha790e867p53", "henq6w86isy3", "egp9o986r"}
--- PASS: TestCollisionCUIDSlug (70.30s)
=== RUN   TestCollisionCUIDNewCrypto
    collision_test.go:23: [cuid.NewCrypto(rand.Reader)] No collision occurs through #[100000000] iterations
    collision_test.go:37: [cuid.NewCrypto(rand.Reader)] Example output => []string{"ckosat9fjg8k28596uv0c3eoy", "ckosashut5jbi8596t2zyrh18", "ckosastvxhb7p8596zogh6eam", "ckosasuondhkz8596oyvgu9sf", "ckosau0ivp9zd8596ha0jku55"}
--- PASS: TestCollisionCUIDNewCrypto (116.19s)
=== RUN   TestCollisionNanoID
    collision_test.go:23: [gonanoid.New()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [gonanoid.New()] Example output => []string{"e71VKEGTiY4b86IR62avd", "NuRANiaeikRFUusp9MNrs", "dkeHoPrezI1wZAMncQYAV", "nqKbsho7CVdokQfX4kiG3", "O7Nx5Y39BtyzToIUIWAkT"}
--- PASS: TestCollisionNanoID (108.15s)
=== RUN   TestCollisionHashids
    collision_test.go:23: [hashids.New()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [hashids.New()] Example output => []string{"Y6A4MW", "007E23", "OPVNNR", "jMrZ9y", "64llKV"}
--- PASS: TestCollisionHashids (107.42s)
=== RUN   TestCollisionSHA256
    collision_test.go:23: [sha256.Sum256()] No collision occurs through #[100000000] iterations
    collision_test.go:37: [sha256.Sum256()] Example output => []string{"Ld/skjHfqpC8pzJ5/dZgFgsYS6fdIJ6kfDaXidZZhdk=", "SbtcUnTAhyfQeCY3ThJ7wftHb5R5wHFZdQzrUJFgPvc=", "NtNRpLtbAwGB0YLzBBPROeupYR0oEco5b9LDYexYB4M=", "kluoZksvasIlxfrQv63SajnIGBFb91UhNqf3uEJKASE=", "BVaDIoBRF1GiKEHfKVSNb3UDcbX1GdebQSOR/xAtfCU="}
--- PASS: TestCollisionSHA256 (183.74s)
PASS
ok  	github.com/evalphobia/benchmark-uuid	749.048s

environment

MacBook Pro (13-inch, M1, 2020)

  • OS: macOS 11.2.3
  • CPU: Apple M1
  • Memory: 16GB
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"strconv"
"testing"
"time"
"github.com/google/uuid"
"github.com/lucsky/cuid"
gonanoid "github.com/matoous/go-nanoid/v2"
"github.com/speps/go-hashids/v2"
)
func BenchmarkUUID(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
uuid.NewString()
}
}
func BenchmarkCUIDNew(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
cuid.New()
}
}
func BenchmarkCUIDSlug(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
cuid.Slug()
}
}
func BenchmarkCUIDNewCrypto(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
cuid.NewCrypto(rand.Reader)
}
}
func BenchmarkNanoID(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
gonanoid.New()
}
}
// ref: Hashids; using UnixNano for a parameter
func BenchmarkHashids(b *testing.B) {
h, _ := hashids.New()
b.ResetTimer()
for i := 0; i < b.N; i++ {
h.EncodeInt64([]int64{time.Now().UnixNano()})
}
}
// ref: SHA256 + base64; using UnixNano for a parameter
func BenchmarkSHA256(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
b := sha256.Sum256([]byte(strconv.FormatInt(time.Now().UnixNano(), 10)))
base64.StdEncoding.EncodeToString(b[:])
}
}
package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"strconv"
"testing"
"github.com/google/uuid"
"github.com/lucsky/cuid"
gonanoid "github.com/matoous/go-nanoid/v2"
"github.com/speps/go-hashids/v2"
)
const maxCount = 100 * 1000 * 1000 // 100M
func collisionError(t *testing.T, name string, i int) {
t.Errorf("[%s] Collision is detected on loop #[%d]", name, i)
}
func logSuccess(t *testing.T, name string) {
t.Logf("[%s] No collision occurs through #[%d] iterations", name, maxCount)
}
func exampleOutput(t *testing.T, name string, m map[string]struct{}) {
const size = 5
list := make([]string, 0, size)
count := 0
for key := range m {
if count >= size {
break
}
list = append(list, key)
count++
}
t.Logf("[%s] Example output => %#v", name, list)
}
func TestCollisionUUID(t *testing.T) {
name := "uuid.NewString()"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v := uuid.NewString()
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
func TestCollisionCUIDNew(t *testing.T) {
name := "cuid.New()"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v := cuid.New()
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
func TestCollisionCUIDSlug(t *testing.T) {
name := "cuid.Slug()"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v := cuid.Slug()
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
func TestCollisionCUIDNewCrypto(t *testing.T) {
name := "cuid.NewCrypto(rand.Reader)"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v, _ := cuid.NewCrypto(rand.Reader)
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
func TestCollisionNanoID(t *testing.T) {
name := "gonanoid.New()"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v, _ := gonanoid.New()
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
// ref: Hashids; using iteration count (=> i) for a parameter
func TestCollisionHashids(t *testing.T) {
name := "hashids.New()"
h, _ := hashids.New()
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
v, _ := h.Encode([]int{i})
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
// ref: SHA256 + base64; using iteration count (=> i) for a parameter
func TestCollisionSHA256(t *testing.T) {
name := "sha256.Sum256()"
m := make(map[string]struct{}, maxCount)
for i := 0; i < maxCount; i++ {
b := sha256.Sum256([]byte(strconv.Itoa(i)))
v := base64.StdEncoding.EncodeToString(b[:])
if _, ok := m[v]; ok {
collisionError(t, name, i)
return
}
m[v] = struct{}{}
}
logSuccess(t, name)
exampleOutput(t, name, m)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment