diff --git a/bitset.go b/bitset.go index 800e01b..6cc2870 100644 --- a/bitset.go +++ b/bitset.go @@ -257,6 +257,50 @@ func (b *BitSet) NextSet(i uint) (uint, bool) { // } // func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) { + myanswer := buffer + capacity := cap(buffer) + x := int(i >> log2WordSize) + if x >= len(b.set) || capacity == 0 { + return 0, myanswer[:0] + } + word := b.set[x] + skip := i & (wordSize - 1) + word = (word >> skip) << skip + myanswer = myanswer[:capacity] + size := int(0) + for word != 0 { + r := trailingZeroes64(word) + + t := word & ((^word) + 1) + myanswer[size] = r + uint(x<<6) + size++ + if size == capacity { + goto End + } + word = word ^ t + } + for idx, word := range b.set[x+1:] { + //base += 64 + for word != 0 { + r := trailingZeroes64(word) + t := word & ((^word) + 1) + myanswer[size] = r + uint(x + idx + 1) * 64 //base + size++ + if size == capacity { + goto End + } + word = word ^ t + } + } +End: + if size > 0 { + return myanswer[size-1], myanswer[:size] + } else { + return 0, myanswer[:0] + } +} + +func (b *BitSet) NextSetManyold(i uint, buffer []uint) (uint, []uint) { myanswer := buffer[:0] x := int(i >> log2WordSize) if x >= len(b.set) { @@ -269,8 +313,9 @@ func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) { capacity := cap(buffer) for len(myanswer) < capacity { for w != 0 { - t := w & ((^w) + 1) r := trailingZeroes64(w) + + t := w & ((^w) + 1) myanswer = append(myanswer, r+base) if len(myanswer) == capacity { goto End diff --git a/bitset_benchmark_test.go b/bitset_benchmark_test.go index a2c8f48..32a9841 100644 --- a/bitset_benchmark_test.go +++ b/bitset_benchmark_test.go @@ -162,9 +162,9 @@ func BenchmarkLemireIterateManyb(b *testing.B) { for v := uint(0); v <= 100000000; v += 100 { bitmap.Set(v) } + buffer := make([]uint, 256) b.ResetTimer() sum := uint(0) - buffer := make([]uint, 256) for i := 0; i < b.N; i++ { j := uint(0) j, buffer = bitmap.NextSetMany(j, buffer) @@ -196,10 +196,9 @@ func BenchmarkFlorianUekermannIterateMany(b *testing.B) { var input = make([]uint64, 68) setRnd(input, 4) var bitmap = From(input) + buffer := make([]uint, 256) b.ResetTimer() - var checksum = uint(0) for i := 0; i < b.N; i++ { - buffer := make([]uint, 256) var last, batch = bitmap.NextSetMany(0, buffer) for len(batch) > 0 { for _, idx := range batch { @@ -212,13 +211,33 @@ func BenchmarkFlorianUekermannIterateMany(b *testing.B) { return } } +// go test -bench=BenchmarkFlorianUekermannIterateMany +func BenchmarkFlorianUekermannIterateManyold(b *testing.B) { + var input = make([]uint64, 68) + setRnd(input, 4) + var bitmap = From(input) + buffer := make([]uint, 256) + b.ResetTimer() + for i := 0; i < b.N; i++ { + var last, batch = bitmap.NextSetManyold(0, buffer) + for len(batch) > 0 { + for _, idx := range batch { + checksum += idx + } + last, batch = bitmap.NextSetManyold(last+1, batch) + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + func BenchmarkFlorianUekermannIterateManyReg(b *testing.B) { var input = make([]uint64, 68) setRnd(input, 4) var bitmap = From(input) b.ResetTimer() - var 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 @@ -229,13 +248,13 @@ 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) for word != 0 { var bitIdx = uint(trailingZeroes64(word)) word ^= 1 << bitIdx - // Do something with the result of the next line var index = wordIdx + bitIdx checksum += index } @@ -247,7 +266,6 @@ func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) { var input = make([]uint64, 68) setRnd(input, 4) b.ResetTimer() - var checksum = uint(0) for i := 0; i < b.N; i++ { checksum += good(input) } @@ -255,3 +273,171 @@ func BenchmarkFlorianUekermannIterateManyComp(b *testing.B) { return } } + +// go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany +func BenchmarkFlorianUekermannLowDensityIterateMany(b *testing.B) { + var input = make([]uint64, 1000000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 50000; i++ { + input[rnd.Uint64()%1000000] = 1 + } + var bitmap = From(input) + buffer := make([]uint, 256) + b.ResetTimer() + for i := 0; i < b.N; i++ { + var last, batch = bitmap.NextSetMany(0, buffer) + for len(batch) > 0 { + for _, idx := range batch { + checksum += idx + } + last, batch = bitmap.NextSetMany(last+1, batch) + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +// go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany +func BenchmarkFlorianUekermannLowDensityIterateManyold(b *testing.B) { + var input = make([]uint64, 1000000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 50000; i++ { + input[rnd.Uint64()%1000000] = 1 + } + var bitmap = From(input) + buffer := make([]uint, 256) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + var last, batch = bitmap.NextSetManyold(0, buffer) + for len(batch) > 0 { + for _, idx := range batch { + checksum += idx + } + last, batch = bitmap.NextSetManyold(last+1, batch) + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +func BenchmarkFlorianUekermannLowDensityIterateManyReg(b *testing.B) { + var input = make([]uint64, 1000000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 50000; i++ { + input[rnd.Uint64()%1000000] = 1 + } + var bitmap = From(input) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { + checksum += j + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +func BenchmarkFlorianUekermannLowDensityIterateManyComp(b *testing.B) { + var input = make([]uint64, 1000000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 50000; i++ { + input[rnd.Uint64()%1000000] = 1 + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + checksum += good(input) + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + + + +// go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany +func BenchmarkFlorianUekermannMidDensityIterateMany(b *testing.B) { + var input = make([]uint64, 10000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 30000; i++ { + input[rnd.Uint64()%10000] |= 1 << rnd.Uint64() + } + var bitmap = From(input) + buffer := make([]uint, 256) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + var last, batch = bitmap.NextSetMany(0, buffer) + for len(batch) > 0 { + for _, idx := range batch { + checksum += idx + } + last, batch = bitmap.NextSetMany(last+1, batch) + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +// go test -bench=BenchmarkFlorianUekermannLowDensityIterateMany +func BenchmarkFlorianUekermannMidDensityIterateManyold(b *testing.B) { + var input = make([]uint64, 10000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 30000; i++ { + input[rnd.Uint64()%10000] |= 1 << rnd.Uint64() + } + var bitmap = From(input) + buffer := make([]uint, 256) + b.ResetTimer() + for i := 0; i < b.N; i++ { + var last, batch = bitmap.NextSetManyold(0, buffer) + for len(batch) > 0 { + for _, idx := range batch { + checksum += idx + } + last, batch = bitmap.NextSetManyold(last+1, batch) + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +func BenchmarkFlorianUekermannMidDensityIterateManyReg(b *testing.B) { + var input = make([]uint64, 10000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 30000; i++ { + input[rnd.Uint64()%10000] |= 1 << rnd.Uint64() + } + var bitmap = From(input) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j, e := bitmap.NextSet(0); e; j, e = bitmap.NextSet(j + 1) { + checksum += j + } + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +func BenchmarkFlorianUekermannMidDensityIterateManyComp(b *testing.B) { + var input = make([]uint64, 10000) + var rnd = rand.NewSource(0).(rand.Source64) + for i := 0; i < 30000; i++ { + input[rnd.Uint64()%10000] |= 1 << rnd.Uint64() + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + checksum += good(input) + } + if checksum == 0 { // added just to fool ineffassign + return + } +} + +var checksum = uint(0)