Skip to content
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

simplify and harmonize NextSet and NextSetMany #191

Merged
merged 4 commits into from
Dec 14, 2024
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
68 changes: 38 additions & 30 deletions bitset.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,23 +501,25 @@ func (b *BitSet) NextSet(i uint) (uint, bool) {
if x >= len(b.set) {
return 0, false
}
w := b.set[x]
w = w >> wordsIndex(i)
if w != 0 {
return i + uint(bits.TrailingZeros64(w)), true

// process first (partial) word
word := b.set[x] >> wordsIndex(i)
if word != 0 {
return i + uint(bits.TrailingZeros64(word)), true
}

// process the following full words until next bit is set
x++
// bounds check elimination in the loop
if x < 0 {
if x >= len(b.set) {
return 0, false
}
for x < len(b.set) {
if b.set[x] != 0 {
return uint(x)*wordSize + uint(bits.TrailingZeros64(b.set[x])), true
}
x++

for idx, word := range b.set[x:] {
if word != 0 {
return uint((x+idx)<<log2WordSize + bits.TrailingZeros64(word)), true
}
}

return 0, false
}

Expand All @@ -543,44 +545,50 @@ func (b *BitSet) NextSet(i uint) (uint, bool) {
// However if bitmap.Count() is large, it might be preferable to
// use several calls to NextSetMany, for performance reasons.
func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
myanswer := buffer
capacity := cap(buffer)
result := buffer[:capacity]

x := int(i >> log2WordSize)
if x >= len(b.set) || capacity == 0 {
return 0, myanswer[:0]
return 0, result[:0]
}
skip := wordsIndex(i)
word := b.set[x] >> skip
myanswer = myanswer[:capacity]
size := int(0)

// process first (partial) word
word := b.set[x] >> wordsIndex(i)

size := 0
for word != 0 {
r := uint(bits.TrailingZeros64(word))
t := word & ((^word) + 1)
myanswer[size] = r + i
result[size] = i + uint(bits.TrailingZeros64(word))

size++
if size == capacity {
goto End
return result[size-1], result[:size]
}
word = word ^ t

// clear the rightmost set bit
word &= word - 1
}

// process the following full words
x++
for idx, word := range b.set[x:] {
for word != 0 {
r := uint(bits.TrailingZeros64(word))
t := word & ((^word) + 1)
myanswer[size] = r + (uint(x+idx) << 6)
result[size] = uint((x+idx)<<log2WordSize + bits.TrailingZeros64(word))

size++
if size == capacity {
goto End
return result[size-1], result[:size]
}
word = word ^ t

// clear the rightmost set bit
word &= word - 1
}
}
End:

if size > 0 {
return myanswer[size-1], myanswer[:size]
return result[size-1], result[:size]
}
return 0, myanswer[:0]
return 0, result[:0]
}

// NextClear returns the next clear bit from the specified index,
Expand Down
111 changes: 67 additions & 44 deletions bitset_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func BenchmarkLemireIterateManyb(b *testing.B) {
}

func setRnd(bits []uint64, halfings int) {
var rnd = rand.NewSource(0).(rand.Source64)
rnd := rand.NewSource(0).(rand.Source64)
for i := range bits {
bits[i] = 0xFFFFFFFFFFFFFFFF
for j := 0; j < halfings; j++ {
Expand All @@ -294,14 +294,14 @@ func setRnd(bits []uint64, halfings int) {

// go test -bench=BenchmarkFlorianUekermannIterateMany
func BenchmarkFlorianUekermannIterateMany(b *testing.B) {
var input = make([]uint64, 68)
input := make([]uint64, 68)
setRnd(input, 4)
var bitmap = From(input)
bitmap := From(input)
buffer := make([]uint, 256)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
var last, batch = bitmap.NextSetMany(0, buffer)
last, batch := bitmap.NextSetMany(0, buffer)
for len(batch) > 0 {
for _, idx := range batch {
checksum += idx
Expand All @@ -315,11 +315,11 @@ func BenchmarkFlorianUekermannIterateMany(b *testing.B) {
}

func BenchmarkFlorianUekermannIterateManyReg(b *testing.B) {
var input = make([]uint64, 68)
input := make([]uint64, 68)
setRnd(input, 4)
var bitmap = From(input)
bitmap := From(input)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
checksum += j
Expand All @@ -333,22 +333,22 @@ func BenchmarkFlorianUekermannIterateManyReg(b *testing.B) {
// function provided by FlorianUekermann
func good(set []uint64) (checksum uint) {
for wordIdx, word := range set {
var wordIdx = uint(wordIdx * 64)
wordIdx := uint(wordIdx * 64)
for word != 0 {
var bitIdx = uint(bits.TrailingZeros64(word))
bitIdx := uint(bits.TrailingZeros64(word))
word ^= 1 << bitIdx
var index = wordIdx + bitIdx
index := wordIdx + bitIdx
checksum += index
}
}
return checksum
}

func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) {
var input = make([]uint64, 68)
input := make([]uint64, 68)
setRnd(input, 4)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
checksum += good(input)
}
Expand All @@ -361,15 +361,15 @@ func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) {

// go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany
func BenchmarkFlorianUekermannLowDensityIterateMany(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 50000; i++ {
input[rnd.Uint64()%1000000] = 1
}
var bitmap = From(input)
bitmap := From(input)
buffer := make([]uint, 256)
b.ResetTimer()
var sum = uint(0)
sum := uint(0)
for i := 0; i < b.N; i++ {
j := uint(0)
j, buffer = bitmap.NextSetMany(j, buffer)
Expand All @@ -386,14 +386,14 @@ func BenchmarkFlorianUekermannLowDensityIterateMany(b *testing.B) {
}

func BenchmarkFlorianUekermannLowDensityIterateManyReg(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 50000; i++ {
input[rnd.Uint64()%1000000] = 1
}
var bitmap = From(input)
bitmap := From(input)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
checksum += j
Expand All @@ -405,13 +405,13 @@ func BenchmarkFlorianUekermannLowDensityIterateManyReg(b *testing.B) {
}

func BenchmarkFlorianUekermannLowDensityIterateManyComp(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 50000; i++ {
input[rnd.Uint64()%1000000] = 1
}
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
checksum += good(input)
}
Expand All @@ -424,12 +424,12 @@ func BenchmarkFlorianUekermannLowDensityIterateManyComp(b *testing.B) {

// go test -bench=BenchmarkFlorianUekermannMidDensityIterateMany
func BenchmarkFlorianUekermannMidDensityIterateMany(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 3000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
var bitmap = From(input)
bitmap := From(input)
buffer := make([]uint, 256)
b.ResetTimer()
sum := uint(0)
Expand All @@ -450,14 +450,14 @@ func BenchmarkFlorianUekermannMidDensityIterateMany(b *testing.B) {
}

func BenchmarkFlorianUekermannMidDensityIterateManyReg(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 3000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
var bitmap = From(input)
bitmap := From(input)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
checksum += j
Expand All @@ -469,13 +469,13 @@ func BenchmarkFlorianUekermannMidDensityIterateManyReg(b *testing.B) {
}

func BenchmarkFlorianUekermannMidDensityIterateManyComp(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 3000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
checksum += good(input)
}
Expand All @@ -487,12 +487,12 @@ func BenchmarkFlorianUekermannMidDensityIterateManyComp(b *testing.B) {
////////// High density

func BenchmarkFlorianUekermannMidStrongDensityIterateMany(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 20000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
var bitmap = From(input)
bitmap := From(input)
buffer := make([]uint, 256)
b.ResetTimer()
sum := uint(0)
Expand All @@ -513,14 +513,14 @@ func BenchmarkFlorianUekermannMidStrongDensityIterateMany(b *testing.B) {
}

func BenchmarkFlorianUekermannMidStrongDensityIterateManyReg(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 20000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
var bitmap = From(input)
bitmap := From(input)
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) {
checksum += j
Expand All @@ -532,13 +532,13 @@ func BenchmarkFlorianUekermannMidStrongDensityIterateManyReg(b *testing.B) {
}

func BenchmarkFlorianUekermannMidStrongDensityIterateManyComp(b *testing.B) {
var input = make([]uint64, 1000000)
var rnd = rand.NewSource(0).(rand.Source64)
input := make([]uint64, 1000000)
rnd := rand.NewSource(0).(rand.Source64)
for i := 0; i < 20000000; i++ {
input[rnd.Uint64()%1000000] |= uint64(1) << (rnd.Uint64() % 64)
}
b.ResetTimer()
var checksum = uint(0)
checksum := uint(0)
for i := 0; i < b.N; i++ {
checksum += good(input)
}
Expand Down Expand Up @@ -635,3 +635,26 @@ func BenchmarkIsSuperSet(b *testing.B) {
len, len, density, overrideS, overrideSS, fStrict)
}
}

// clear the right most bit (C-RMS)
// test two different algorithms
func BenchmarkClearRMS(b *testing.B) {
var word uint64

// cryptic
b.Run("cryptic", func(b *testing.B) {
word = 0xaaaa_aaaa_aaaa_aaaa
for i := 0; i < b.N; i++ {
t := word & ((^word) + 1)
word = word ^ t
}
})

// less cryptic
b.Run("simple", func(b *testing.B) {
word = 0xaaaa_aaaa_aaaa_aaaa
for i := 0; i < b.N; i++ {
word &= word - 1
}
})
}
Loading
Loading