Commit message:
uuid: use bytes.Compare in UUID.Compare
UUID.Compare compared the UUIDs one byte at a time, so its cost grew
with the length of the common prefix. bytes.Compare produces the same
lexicographic (RFC 9562 Section 6.11) ordering and, on arm64, compares
the 16 bytes as two 64-bit words, taking roughly constant time.
UUIDv7 values begin with a big-endian millisecond timestamp, so UUIDs
generated close in time share long prefixes, which is the common case
when sorting them or using them as keys in ordered maps.
Benchmark:
func BenchmarkCompare(b *testing.B) {
u := uuid.MustParse("01a148bf-a08a-75a1-8422-c1a47b40eec7")
for _, bc := range []struct {
name string
diffIndex int // index of the differing byte, or -1 if equal
}{
{"Equal", -1},
{"Diff0", 0},
{"Diff1", 1},
{"Diff2", 2},
{"Diff3", 3},
{"Diff7", 7},
{"Diff15", 15},
} {
v := u
if bc.diffIndex >= 0 {
v[bc.diffIndex]++
}
b.Run(bc.name, func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
u.Compare(v)
}
})
}
}
goos: darwin
goarch: arm64
pkg: uuid
cpu: Apple M4 Max
│ old │ new │
│ sec/op │ sec/op vs base │
Compare/Equal-16 10.680n ± 9% 1.864n ± 0% -82.55% (p=0.000 n=20)
Compare/Diff0-16 1.788n ± 1% 1.865n ± 0% +4.25% (p=0.000 n=20)
Compare/Diff1-16 1.810n ± 1% 1.864n ± 0% +2.98% (p=0.000 n=20)
Compare/Diff2-16 2.488n ± 4% 1.867n ± 0% -24.94% (p=0.000 n=20)
Compare/Diff3-16 3.395n ± 5% 1.864n ± 0% -45.09% (p=0.000 n=20)
Compare/Diff7-16 5.888n ± 7% 1.869n ± 1% -68.27% (p=0.000 n=20)
Compare/Diff15-16 11.070n ± 8% 1.875n ± 0% -83.07% (p=0.000 n=20)
geomean 4.087n 1.867n -54.32%
│ old │ new │
│ B/op │ B/op vs base │
Compare/Equal-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff0-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff1-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff2-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff3-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff7-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff15-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
│ old │ new │
│ allocs/op │ allocs/op vs base │
Compare/Equal-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff0-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff1-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff2-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff3-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff7-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
Compare/Diff15-16 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹
geomean ² +0.00% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
Fixes #82288