Skip to content

Commit

Permalink
Use the len64 function to accelerate PreviousSet and PreviousClear
Browse files Browse the repository at this point in the history
On an Apple M2 our benchmark produces these results:

BenchmarkBitsetOps/NextSet-8       	 2552749	       468.1 ns/op
BenchmarkBitsetOps/NextClear-8     	 2571921	       466.9 ns/op
BenchmarkBitsetOps/PreviousSet-8   	 1760882	       682.4 ns/op
BenchmarkBitsetOps/PreviousClear-8 	 1763605	       680.0 ns/op

Which greatly improves on the prior results of

    BenchmarkBitsetOps/PreviousSet-8           19930             59924 ns/op
    BenchmarkBitsetOps/PreviousClear-8         19912             61002 ns/op
  • Loading branch information
lukesandberg committed Nov 15, 2024
1 parent de30b18 commit cbd10cc
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 16 deletions.
50 changes: 36 additions & 14 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,23 @@ func (b *BitSet) PreviousSet(i uint) (uint, bool) {
if x >= len(b.set) {
return 0, false
}
for {
if b.Test(i) {
return i, true
}
if i == 0 {
break
w := b.set[x]
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
x--
// bounds check elimination in the loop
if x < 0 {
return 0, false
}
for x >= 0 {
w = b.set[x]
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
i--
x--
}
return 0, false
}
Expand All @@ -614,14 +623,27 @@ func (b *BitSet) PreviousClear(i uint) (uint, bool) {
if x >= len(b.set) {
return 0, false
}
for {
if !b.Test(i) {
return i, true
}
if i == 0 {
break
w := b.set[x]
// Flip all bits and find the highest one bit
w = ^w
// Clear the bits above the index
w = w & ((1 << (wordsIndex(i) + 1)) - 1)
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}

x--
// bounds check elimination in the loop
if x < 0 {
return 0, false
}
for x >= 0 {
w = b.set[x]
w = ^w
if w != 0 {
return uint(x<<log2WordSize) + len64(w) - 1, true
}
i--
x--
}
return 0, false
}
Expand Down
12 changes: 10 additions & 2 deletions bitset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2089,10 +2089,11 @@ func TestWord(t *testing.T) {
}

func TestPreviousSet(t *testing.T) {
v := New(10)
v := New(128)
v.Set(0)
v.Set(2)
v.Set(4)
v.Set(120)
for _, tt := range []struct {
index uint
want uint
Expand All @@ -2104,6 +2105,9 @@ func TestPreviousSet(t *testing.T) {
{3, 2, true},
{4, 4, true},
{5, 4, true},
{100, 4, true},
{120, 120, true},
{121, 120, true},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
Expand All @@ -2116,10 +2120,11 @@ func TestPreviousSet(t *testing.T) {
}

func TestPreviousClear(t *testing.T) {
v := New(10)
v := New(128)
v.Set(0)
v.Set(2)
v.Set(4)
v.Set(120)
for _, tt := range []struct {
index uint
want uint
Expand All @@ -2131,6 +2136,9 @@ func TestPreviousClear(t *testing.T) {
{3, 3, true},
{4, 3, true},
{5, 5, true},
{100, 100, true},
{120, 119, true},
{121, 121, true},
{1024, 0, false},
} {
t.Run(fmt.Sprintf("@%d", tt.index), func(t *testing.T) {
Expand Down

0 comments on commit cbd10cc

Please sign in to comment.