From 4fb57189e3b61a727f88915728648514cf713889 Mon Sep 17 00:00:00 2001 From: WebChang <40751057+Web-Coke@users.noreply.github.com> Date: Tue, 13 May 2025 14:43:00 +0800 Subject: [PATCH 1/3] Update ulid.go Performance Optimization --- ulid.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ulid.go b/ulid.go index 77e9ddd..bf819c8 100644 --- a/ulid.go +++ b/ulid.go @@ -15,7 +15,6 @@ package ulid import ( "bufio" - "bytes" "database/sql/driver" "encoding/binary" "errors" @@ -25,6 +24,7 @@ import ( "math/rand" "sync" "time" + "unsafe" ) /* @@ -493,7 +493,23 @@ func (id *ULID) SetEntropy(e []byte) error { // Compare returns an integer comparing id and other lexicographically. // The result will be 0 if id==other, -1 if id < other, and +1 if id > other. func (id ULID) Compare(other ULID) int { - return bytes.Compare(id[:], other[:]) + ih := *(*uint64)(unsafe.Pointer(&id[0])) + il := *(*uint64)(unsafe.Pointer(&id[8])) + oh := *(*uint64)(unsafe.Pointer(&other[0])) + ol := *(*uint64)(unsafe.Pointer(&other[8])) + if ih > oh { + return 1 + } + if ih < oh { + return -1 + } + if il > ol { + return 1 + } + if il < ol { + return -1 + } + return 0 } // Scan implements the sql.Scanner interface. It supports scanning From 2ac0fa0ef90e8188dd124efb1bf7f16b584905b5 Mon Sep 17 00:00:00 2001 From: WebChang <40751057+Web-Coke@users.noreply.github.com> Date: Fri, 16 May 2025 02:08:40 +0000 Subject: [PATCH 2/3] Correct endianness: ulid.go Add benchmark tests for new and old comparison functions: ulid_test.go --- ulid.go | 9 ++++----- ulid_test.go | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/ulid.go b/ulid.go index bf819c8..15a0e41 100644 --- a/ulid.go +++ b/ulid.go @@ -24,7 +24,6 @@ import ( "math/rand" "sync" "time" - "unsafe" ) /* @@ -493,10 +492,10 @@ func (id *ULID) SetEntropy(e []byte) error { // Compare returns an integer comparing id and other lexicographically. // The result will be 0 if id==other, -1 if id < other, and +1 if id > other. func (id ULID) Compare(other ULID) int { - ih := *(*uint64)(unsafe.Pointer(&id[0])) - il := *(*uint64)(unsafe.Pointer(&id[8])) - oh := *(*uint64)(unsafe.Pointer(&other[0])) - ol := *(*uint64)(unsafe.Pointer(&other[8])) + ih := binary.NativeEndian.Uint64(id[0:8]) + il := binary.NativeEndian.Uint64(id[8:16]) + oh := binary.NativeEndian.Uint64(other[0:8]) + ol := binary.NativeEndian.Uint64(other[8:16]) if ih > oh { return 1 } diff --git a/ulid_test.go b/ulid_test.go index 5ca70ae..a78cb4a 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -892,7 +892,20 @@ func BenchmarkSetEntropy(b *testing.B) { } } -func BenchmarkCompare(b *testing.B) { +func OldCompare(id, other ulid.ULID) int { + return bytes.Compare(id[:], other[:]) +} + +func BenchmarkOldCompare(b *testing.B) { + id, other := ulid.MustNew(12345, nil), ulid.MustNew(54321, nil) + b.SetBytes(int64(len(id) * 2)) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = OldCompare(id, other) + } +} + +func BenchmarkNewCompare(b *testing.B) { id, other := ulid.MustNew(12345, nil), ulid.MustNew(54321, nil) b.SetBytes(int64(len(id) * 2)) b.ResetTimer() From 36b9df24e0ceb20e72fc72e572a591a3520c5908 Mon Sep 17 00:00:00 2001 From: WebChang <40751057+Web-Coke@users.noreply.github.com> Date: Fri, 16 May 2025 02:55:18 +0000 Subject: [PATCH 3/3] Big Endian mode: compare_be.go Little Endian mode: compare_le.go Move the Compare function to BE mode or LE mode: ulid.go --- compare_be.go | 25 +++++++++++++++++++++++++ compare_le.go | 25 +++++++++++++++++++++++++ ulid.go | 22 ---------------------- 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 compare_be.go create mode 100644 compare_le.go diff --git a/compare_be.go b/compare_be.go new file mode 100644 index 0000000..92d757c --- /dev/null +++ b/compare_be.go @@ -0,0 +1,25 @@ +//go:build armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64 + +package ulid + +// Compare returns an integer comparing id and other lexicographically. +// The result will be 0 if id==other, -1 if id < other, and +1 if id > other. +func (id ULID) Compare(other ULID) int { + ih := uint64(id[0x00]) | uint64(id[0x01])<<8 | uint64(id[0x02])<<16 | uint64(id[0x03])<<24 | uint64(id[0x04])<<32 | uint64(id[0x05])<<40 | uint64(id[0x06])<<48 | uint64(id[0x07])<<56 + oh := uint64(other[0x00]) | uint64(other[0x01])<<8 | uint64(other[0x02])<<16 | uint64(other[0x03])<<24 | uint64(other[0x04])<<32 | uint64(other[0x05])<<40 | uint64(other[0x06])<<48 | uint64(other[0x07])<<56 + if ih > oh { + return 1 + } + if ih < oh { + return -1 + } + il := uint64(id[0x08]) | uint64(id[0x09])<<8 | uint64(id[0x0A])<<16 | uint64(id[0x0B])<<24 | uint64(id[0x0C])<<32 | uint64(id[0x0D])<<40 | uint64(id[0x0E])<<48 | uint64(id[0x0F])<<56 + ol := uint64(other[0x08]) | uint64(other[0x09])<<8 | uint64(other[0x0A])<<16 | uint64(other[0x0B])<<24 | uint64(other[0x0C])<<32 | uint64(other[0x0D])<<40 | uint64(other[0x0E])<<48 | uint64(other[0x0F])<<56 + if il > ol { + return 1 + } + if il < ol { + return -1 + } + return 0 +} diff --git a/compare_le.go b/compare_le.go new file mode 100644 index 0000000..8c2bff1 --- /dev/null +++ b/compare_le.go @@ -0,0 +1,25 @@ +//go:build 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm + +package ulid + +// Compare returns an integer comparing id and other lexicographically. +// The result will be 0 if id==other, -1 if id < other, and +1 if id > other. +func (id ULID) Compare(other ULID) int { + ih := uint64(id[0x07]) | uint64(id[0x06])<<8 | uint64(id[0x05])<<16 | uint64(id[0x04])<<24 | uint64(id[0x03])<<32 | uint64(id[0x02])<<40 | uint64(id[0x01])<<48 | uint64(id[0x00])<<56 + oh := uint64(other[0x07]) | uint64(other[0x06])<<8 | uint64(other[0x05])<<16 | uint64(other[0x04])<<24 | uint64(other[0x03])<<32 | uint64(other[0x02])<<40 | uint64(other[0x01])<<48 | uint64(other[0x00])<<56 + if ih > oh { + return 1 + } + if ih < oh { + return -1 + } + il := uint64(id[0x0F]) | uint64(id[0x0E])<<8 | uint64(id[0x0D])<<16 | uint64(id[0x0C])<<24 | uint64(id[0x0B])<<32 | uint64(id[0x0A])<<40 | uint64(id[0x09])<<48 | uint64(id[0x08])<<56 + ol := uint64(other[0x0F]) | uint64(other[0x0E])<<8 | uint64(other[0x0D])<<16 | uint64(other[0x0C])<<24 | uint64(other[0x0B])<<32 | uint64(other[0x0A])<<40 | uint64(other[0x09])<<48 | uint64(other[0x08])<<56 + if il > ol { + return 1 + } + if il < ol { + return -1 + } + return 0 +} diff --git a/ulid.go b/ulid.go index 15a0e41..0b05803 100644 --- a/ulid.go +++ b/ulid.go @@ -489,28 +489,6 @@ func (id *ULID) SetEntropy(e []byte) error { return nil } -// Compare returns an integer comparing id and other lexicographically. -// The result will be 0 if id==other, -1 if id < other, and +1 if id > other. -func (id ULID) Compare(other ULID) int { - ih := binary.NativeEndian.Uint64(id[0:8]) - il := binary.NativeEndian.Uint64(id[8:16]) - oh := binary.NativeEndian.Uint64(other[0:8]) - ol := binary.NativeEndian.Uint64(other[8:16]) - if ih > oh { - return 1 - } - if ih < oh { - return -1 - } - if il > ol { - return 1 - } - if il < ol { - return -1 - } - return 0 -} - // Scan implements the sql.Scanner interface. It supports scanning // a string or byte slice. func (id *ULID) Scan(src interface{}) error {