Skip to content

Commit 3edbc4f

Browse files
committed
perf: Make the UUIDv7 implementation lock-free
1 parent 2d3c2a9 commit 3edbc4f

2 files changed

Lines changed: 39 additions & 15 deletions

File tree

uuid_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,29 @@ func BenchmarkUUID_NewPooled(b *testing.B) {
766766
})
767767
}
768768

769+
func BenchmarkUUID_NewV7(b *testing.B) {
770+
b.RunParallel(func(pb *testing.PB) {
771+
for pb.Next() {
772+
_, err := NewV7()
773+
if err != nil {
774+
b.Fatal(err)
775+
}
776+
}
777+
})
778+
}
779+
780+
func BenchmarkUUID_NewV7Pooled(b *testing.B) {
781+
EnableRandPool()
782+
b.RunParallel(func(pb *testing.PB) {
783+
for pb.Next() {
784+
_, err := NewV7()
785+
if err != nil {
786+
b.Fatal(err)
787+
}
788+
}
789+
})
790+
}
791+
769792
func BenchmarkUUIDs_Strings(b *testing.B) {
770793
uuid1, err := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
771794
if err != nil {

version7.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package uuid
66

77
import (
88
"io"
9+
"sync/atomic"
910
)
1011

1112
// UUID version 7 features a time-ordered value field derived from the widely
@@ -77,28 +78,28 @@ func makeV7(uuid []byte) {
7778
// lastV7time is the last time we returned stored as:
7879
//
7980
// 52 bits of time in milliseconds since epoch
80-
// 12 bits of (fractional nanoseconds) >> 8
81-
var lastV7time int64
81+
// 12 bits of sequence number.
82+
var lastV7time atomic.Int64
8283

8384
const nanoPerMilli = 1000000
8485

85-
// getV7Time returns the time in milliseconds and nanoseconds / 256.
86+
// getV7Time returns the time in milliseconds and a sequence number.
8687
// The returned (milli << 12 + seq) is guaranteed to be greater than
8788
// (milli << 12 + seq) returned by any previous call to getV7Time.
8889
func getV7Time() (milli, seq int64) {
89-
timeMu.Lock()
90-
defer timeMu.Unlock()
90+
milli = timeNow().UnixMilli()
9191

92-
nano := timeNow().UnixNano()
93-
milli = nano / nanoPerMilli
94-
// Sequence number is between 0 and 3906 (nanoPerMilli>>8)
95-
seq = (nano - milli*nanoPerMilli) >> 8
96-
now := milli<<12 + seq
97-
if now <= lastV7time {
98-
now = lastV7time + 1
99-
milli = now >> 12
100-
seq = now & 0xfff
92+
for {
93+
oldVal := lastV7time.Load()
94+
if milli <= (oldVal+1)>>12 {
95+
break
96+
}
97+
lastV7time.CompareAndSwap(oldVal, (milli<<12)-1)
10198
}
102-
lastV7time = now
99+
100+
result := lastV7time.Add(1)
101+
milli = result >> 12
102+
seq = result & 0xfff
103+
103104
return milli, seq
104105
}

0 commit comments

Comments
 (0)