Skip to content

Commit

Permalink
h2 as uint8
Browse files Browse the repository at this point in the history
This doesn't change the logic, but by defining h2 as uint8, and using
uint8 elsewhere, we can work properly with higher bits (and define empty
in it's binary form, instead of having to use the negative value and a
comment). This will also allow us future changes without having to play
with overflows.

Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
  • Loading branch information
colega committed Jul 4, 2024
1 parent d217e89 commit e8b76af
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 21 deletions.
4 changes: 2 additions & 2 deletions bits_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ const (
type bitset uint16

func metaMatchH2(m *metadata, h h2) bitset {
b := simd.MatchMetadata((*[16]int8)(m), int8(h))
b := simd.MatchMetadata((*[groupSize]uint8)(m), uint8(h))
return bitset(b)
}

func metaMatchEmpty(m *metadata) bitset {
b := simd.MatchMetadata((*[16]int8)(m), empty)
b := simd.MatchMetadata((*[groupSize]uint8)(m), empty)
return bitset(b)
}

Expand Down
19 changes: 9 additions & 10 deletions bits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
func TestMatchMetadata(t *testing.T) {
var meta metadata
for i := range meta {
meta[i] = int8(i)
meta[i] = uint8(i)
}
t.Run("metaMatchH2", func(t *testing.T) {
for _, x := range meta {
Expand All @@ -43,7 +43,7 @@ func TestMatchMetadata(t *testing.T) {
mask = metaMatchEmpty(&meta)
assert.NotZero(t, mask)
assert.Equal(t, uint32(i), nextMatch(&mask))
meta[i] = int8(i)
meta[i] = uint8(i)
}
})
t.Run("nextMatch", func(t *testing.T) {
Expand All @@ -54,7 +54,7 @@ func TestMatchMetadata(t *testing.T) {
assert.Equal(t, uint32(i), nextMatch(&mask))
}
for i := 0; i < len(meta); i += 2 {
meta[i] = int8(42)
meta[i] = uint8(42)
}
mask = metaMatchH2(&meta, h2(42))
for i := 0; i < len(meta); i += 2 {
Expand All @@ -66,7 +66,7 @@ func TestMatchMetadata(t *testing.T) {
func BenchmarkMatchMetadata(b *testing.B) {
var meta metadata
for i := range meta {
meta[i] = int8(i)
meta[i] = uint8(i)
}
var mask bitset
for i := 0; i < b.N; i++ {
Expand All @@ -90,14 +90,13 @@ func nextPow2(x uint32) uint32 {
}

func TestConstants(t *testing.T) {
c1, c2 := empty, tombstone
assert.Equal(t, byte(0b1000_0000), byte(c1))
assert.Equal(t, byte(0b1000_0000), reinterpretCast(c1))
assert.Equal(t, byte(0b1111_1110), byte(c2))
assert.Equal(t, byte(0b1111_1110), reinterpretCast(c2))
assert.Equal(t, byte(0b1000_0000), empty)
assert.Equal(t, byte(0b1000_0000), reinterpretCast(empty))
assert.Equal(t, byte(0b1111_1110), tombstone)
assert.Equal(t, byte(0b1111_1110), reinterpretCast(tombstone))
}

func reinterpretCast(i int8) byte {
func reinterpretCast(i uint8) byte {
return *(*byte)(unsafe.Pointer(&i))
}

Expand Down
10 changes: 5 additions & 5 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type Map[K comparable, V any] struct {
// metadata is the h2 metadata array for a group.
// find operations first probe the controls bytes
// to filter candidates before matching keys
type metadata [groupSize]int8
type metadata [groupSize]uint8

// group is a group of 16 key-value pairs
type group[K comparable, V any] struct {
Expand All @@ -47,15 +47,15 @@ type group[K comparable, V any] struct {
const (
h1Mask uint64 = 0xffff_ffff_ffff_ff80
h2Mask uint64 = 0x0000_0000_0000_007f
empty int8 = -128 // 0b1000_0000
tombstone int8 = -2 // 0b1111_1110
empty uint8 = 0b1000_0000
tombstone uint8 = 0b1111_1110
)

// h1 is a 57 bit hash prefix
type h1 uint64

// h2 is a 7 bit hash suffix
type h2 int8
type h2 uint8

// NewMap constructs a Map.
func NewMap[K comparable, V any](sz uint32) (m *Map[K, V]) {
Expand Down Expand Up @@ -150,7 +150,7 @@ func (m *Map[K, V]) Put(key K, value V) {
s := nextMatch(&matches)
m.groups[g].keys[s] = key
m.groups[g].values[s] = value
m.ctrl[g][s] = int8(lo)
m.ctrl[g][s] = uint8(lo)
m.resident++
return
}
Expand Down
4 changes: 2 additions & 2 deletions simd/match.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

#include "textflag.h"

// func MatchMetadata(metadata *[16]int8, hash int8) uint16
// func MatchMetadata(metadata *[16]uint8, hash uint8) uint16
// Requires: SSE2, SSSE3
TEXT ·MatchMetadata(SB), NOSPLIT, $0-18
MOVQ metadata+0(FP), AX
MOVBLSX hash+8(FP), CX
MOVBLZX hash+8(FP), CX
MOVD CX, X0
PXOR X1, X1
PSHUFB X1, X0
Expand Down
2 changes: 1 addition & 1 deletion simd/match_amd64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion simd/src/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
func main() {
ConstraintExpr("amd64")

TEXT("MatchMetadata", NOSPLIT, "func(metadata *[16]int8, hash int8) uint16")
TEXT("MatchMetadata", NOSPLIT, "func(metadata *[16]uint8, hash uint8) uint16")
Doc("MatchMetadata performs a 16-way probe of |metadata| using SSE instructions",
"nb: |metadata| must be an aligned pointer")
m := Mem{Base: Load(Param("metadata"), GP64())}
Expand Down

0 comments on commit e8b76af

Please sign in to comment.