Skip to content

Guid: optimize comparison functions #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 9 additions & 24 deletions sttp/guid/Guid.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package guid

import (
"encoding/binary"
"errors"
"strconv"

Expand All @@ -43,23 +44,17 @@ func New() Guid {

// IsZero determines if the Guid value is its zero value, i.e., empty.
func (g Guid) IsZero() bool {
return Equal(g, Empty)
return g == Empty
}

// Equal returns true if this Guid and other Guid values are equal.
func (g Guid) Equal(other Guid) bool {
return Equal(g, other)
return g == other
}

// Equal returns true if the a and b Guid values are equal.
func Equal(a, b Guid) bool {
for i := 0; i < 16; i++ {
if a[i] != b[i] {
return false
}
}

return true
return a == b
}

// Compare returns an integer comparing this Guid (g) to other Guid. The result will be 0 if g==other, -1 if this g < other, and +1 if g > other.
Expand Down Expand Up @@ -103,18 +98,10 @@ func result(left, right uint32) int {

// Components gets the Guid value as its constituent components.
func (g Guid) Components() (a uint32, b, c uint16, d [8]byte) {
a = (uint32(g[0]) << 24) | (uint32(g[1]) << 16) | (uint32(g[2]) << 8) | uint32(g[3])
b = uint16((uint32(g[4]) << 8) | uint32(g[5]))
c = uint16((uint32(g[6]) << 8) | uint32(g[7]))
d[0] = g[8]
d[1] = g[9]
d[2] = g[10]
d[3] = g[11]
d[4] = g[12]
d[5] = g[13]
d[6] = g[14]
d[7] = g[15]

a = binary.BigEndian.Uint32(g[:4])
b = binary.BigEndian.Uint16(g[4:6])
c = binary.BigEndian.Uint16(g[6:8])
copy(d[:], g[8:16])
return
}

Expand Down Expand Up @@ -143,9 +130,7 @@ func FromBytes(data []byte, swapEndianness bool) (Guid, error) {
swapGuidEndianness(&data)
}

var g Guid
copy(g[:], data[:16])
return g, nil
return Guid(data), nil
}

// ToBytes creates a byte slice from a Guid.
Expand Down
113 changes: 105 additions & 8 deletions sttp/guid/Guid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ package guid

import (
"bytes"
"strconv"
"testing"
"unsafe"
)

const (
Expand All @@ -39,7 +39,7 @@ const (
gsz string = "{00000000-0000-0000-0000-000000000000}"
)

//gocyclo: ignore
// gocyclo: ignore
func TestGuidParsing(t *testing.T) {
var g1, g2, g3, g4 Guid
var err error
Expand Down Expand Up @@ -113,12 +113,12 @@ func TestGuidParsing(t *testing.T) {
func TestNewGuidRandomness(t *testing.T) {
for i := 0; i < 10000; i++ {
if New().Equal(New()) || Equal(New(), New()) {
t.Fatalf("TestNewGuidRandomness: encountered non-unique Guid after " + strconv.Itoa(i*4) + "generations")
t.Fatalf("TestNewGuidRandomness: encountered non-unique Guid after %d generations", i*4)
}
}
}

//gocyclo: ignore
// gocyclo: ignore
func TestZeroGuid(t *testing.T) {
var gz, zero Guid
var err error
Expand Down Expand Up @@ -166,7 +166,7 @@ func TestZeroGuid(t *testing.T) {
}
}

//gocyclo: ignore
// gocyclo: ignore
func TestGuidCompare(t *testing.T) {
var g1, g2, g3, g4, g5, g6 Guid
var err error
Expand Down Expand Up @@ -296,16 +296,113 @@ func testGuidToFromBytes(g Guid, gs string, swapEndianness bool, t *testing.T) {
}

if !bytes.Equal(gbf, gbs) {
t.Fatalf("TestGuidToFromBytes: ToBytes test compare failed for guid " + gs)
t.Fatal("TestGuidToFromBytes: ToBytes test compare failed for guid " + gs)
}

g1fb, err := FromBytes(gbf, swapEndianness)

if err != nil {
t.Fatalf("TestGuidToFromBytes: FromBytes failed for guid " + gs)
t.Fatal("TestGuidToFromBytes: FromBytes failed for guid " + gs)
}

if !g1fb.Equal(g) {
t.Fatalf("TestGuidToFromBytes: FromBytes test compare failed for guid " + gs)
t.Fatal("TestGuidToFromBytes: FromBytes test compare failed for guid " + gs)
}
}

func (g Guid) EqualIndirectBaseline(other Guid) bool {
return EqualBaseline(g, other)
}

func (g Guid) EqualIndirect(other Guid) bool {
return Equal(g, other)
}

func EqualBaseline(a, b Guid) bool {
for k := range 16 {
if a[k] != b[k] {
return false
break
}
}
return true

}

func BenchmarkEqualityIndirect(b *testing.B) {
list := []string{gs1, gs2, gs3, gs4, gs5, gs6, gsz}
glist := [7]Guid{}
for i := range list {
glist[i], _ = Parse(list[i])
}
b.ResetTimer()
for range b.N {
a, b := glist[0], glist[1]
equal := a.EqualIndirect(b)
_ = equal
}
}

func BenchmarkEqualityIndirectBaseline(b *testing.B) {
list := []string{gs1, gs2, gs3, gs4, gs5, gs6, gsz}
glist := [7]Guid{}
for i := range list {
glist[i], _ = Parse(list[i])
}
b.ResetTimer()
for range b.N {
a, b := glist[0], glist[1]
equal := a.EqualIndirectBaseline(b)
_ = equal
}
}

func BenchmarkEqualityBaseline(b *testing.B) {
list := []string{gs1, gs2, gs3, gs4, gs5, gs6, gsz}
glist := [7]Guid{}
for i := range list {
glist[i], _ = Parse(list[i])
}
b.ResetTimer()
for range b.N {
equal := true
a, b := glist[0], glist[1]
for k := range 16 {
if a[k] != b[k] {
equal = false
break
}
}
_ = equal
}
}

func BenchmarkEqualityCurrent(b *testing.B) {
list := []string{gs1, gs2, gs3, gs4, gs5, gs6, gsz}
glist := [7]Guid{}
for i := range list {
glist[i], _ = Parse(list[i])
}
b.ResetTimer()
for range b.N {
a1 := (*uint64)(unsafe.Pointer(&glist[0][0]))
a2 := (*uint64)(unsafe.Pointer(&glist[0][8]))
b1 := (*uint64)(unsafe.Pointer(&glist[1][0]))
b2 := (*uint64)(unsafe.Pointer(&glist[1][8]))
equal := *a1 == *b1 && *a2 == *b2
_ = equal
}
}

func BenchmarkEqualityDirect(b *testing.B) {
list := []string{gs1, gs2, gs3, gs4, gs5, gs6, gsz}
glist := [7]Guid{}
for i := range list {
glist[i], _ = Parse(list[i])
}
b.ResetTimer()
for range b.N {
equal := glist[0] == glist[1]
_ = equal
}
}
Loading