Skip to content

Commit 095528b

Browse files
committed
Set/Clear
1 parent b01912e commit 095528b

File tree

3 files changed

+148
-13
lines changed

3 files changed

+148
-13
lines changed

abitvec.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,31 @@ func (bv ABitVec) Get(k uint64) (bool, error) {
8383
bucket, bit := offset(k)
8484
return bv.buckets[bucket]&bit != 0, nil
8585
}
86+
87+
// Set sets bit `k` to true unconditionally.
88+
func (bv ABitVec) Set(k uint64) error {
89+
if k >= bv.capacity {
90+
return errors.New("Attempt to access element beyond vector bounds")
91+
}
92+
bucket, bit := offset(k)
93+
old := atomic.LoadUint64(&bv.buckets[bucket])
94+
retry:
95+
if atomic.CompareAndSwapUint64(&bv.buckets[bucket], old, old|bit) {
96+
return nil
97+
}
98+
goto retry
99+
}
100+
101+
// Clear sets bit `k` to false unconditionally.
102+
func (bv ABitVec) Clear(k uint64) error {
103+
if k >= bv.capacity {
104+
return errors.New("Attempt to access element beyond vector bounds")
105+
}
106+
bucket, bit := offset(k)
107+
old := atomic.LoadUint64(&bv.buckets[bucket])
108+
retry:
109+
if atomic.CompareAndSwapUint64(&bv.buckets[bucket], old, old&(allSet^bit)) {
110+
return nil
111+
}
112+
goto retry
113+
}

bitvec.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
// Package bitvec is bit-vector with atomic and non-atomic access
22
package bitvec
33

4-
import "errors"
4+
import (
5+
"errors"
6+
)
57

68
const (
7-
nbits = 6 // 64 bits in a uint64
8-
ws = 1 << nbits // constant 64
9-
mask = ws - 1 // all ones
9+
nbits = 6 // 64 bits in a uint64
10+
ws = 1 << nbits // constant 64
11+
mask = ws - 1 // all ones
12+
allSet = ^uint64(0)
1013
)
1114

1215
func offset(k uint64) (bucket, bit uint64) {
@@ -52,3 +55,23 @@ func (bv BitVec) Get(k uint64) (bool, error) {
5255
bucket, bit := offset(k)
5356
return bv.buckets[bucket]&bit != 0, nil
5457
}
58+
59+
// Set sets bit `k` to true unconditionally.
60+
func (bv BitVec) Set(k uint64) error {
61+
if k >= bv.capacity {
62+
return errors.New("Attempt to access element beyond vector bounds")
63+
}
64+
bucket, bit := offset(k)
65+
bv.buckets[bucket] |= bit
66+
return nil
67+
}
68+
69+
// Clear sets bit `k` to false unconditionally.
70+
func (bv BitVec) Clear(k uint64) error {
71+
if k >= bv.capacity {
72+
return errors.New("Attempt to access element beyond vector bounds")
73+
}
74+
bucket, bit := offset(k)
75+
bv.buckets[bucket] &= (allSet ^ bit)
76+
return nil
77+
}

bitvec_test.go

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ var setSizes = []int{13, 256, 65536, 16777216}
1414

1515
func TestBV(t *testing.T) {
1616
bv := New(100)
17+
bv2 := New(100)
1718
abv := NewAtomic(100)
19+
abv2 := NewAtomic(100)
1820
t.Run("valid indices", func(t *testing.T) {
1921
for _, k := range GoodSets {
2022
if !bv.TrySet(k) {
@@ -23,13 +25,30 @@ func TestBV(t *testing.T) {
2325
if !abv.TrySet(k) {
2426
t.Errorf("abv could not set good index %d", k)
2527
}
28+
bv2.Set(k)
29+
abv2.Set(k)
2630

2731
if x, _ := bv.Get(k); !x {
2832
t.Errorf("bv bit %d was supposed to be set but wasn't", k)
2933
}
3034
if x, _ := abv.Get(k); !x {
3135
t.Errorf("abv bit %d was supposed to be set but wasn't", k)
3236
}
37+
if x, _ := bv2.Get(k); !x {
38+
t.Errorf("bv2 bit %d was supposed to be set but wasn't", k)
39+
}
40+
if x, _ := abv2.Get(k); !x {
41+
t.Errorf("abv2 bit %d was supposed to be set but wasn't", k)
42+
}
43+
44+
bv.Clear(k)
45+
if x, _ := bv.Get(k); x {
46+
t.Errorf("bv bit %d was supposed to be cleared but wasn't", k)
47+
}
48+
abv.Clear(k)
49+
if x, _ := abv.Get(k); x {
50+
t.Errorf("abv bit %d was supposed to be cleared but wasn't", k)
51+
}
3352
}
3453
})
3554
t.Run("invalid indices", func(t *testing.T) {
@@ -40,12 +59,21 @@ func TestBV(t *testing.T) {
4059
if abv.TrySet(k) {
4160
t.Errorf("abv successfully set bad index %d ", k)
4261
}
43-
}
44-
if _, err := bv.Get(101); err == nil {
45-
t.Errorf("bv error should be thrown for out of bounds access")
46-
}
47-
if _, err := abv.Get(101); err == nil {
48-
t.Errorf("abv error should be thrown for out of bounds access")
62+
if bv2.Set(k) == nil {
63+
t.Errorf("bv2 successfully set bad index %d ", k)
64+
}
65+
if abv2.Set(k) == nil {
66+
t.Errorf("abv2 successfully set bad index %d ", k)
67+
}
68+
if _, err := bv.Get(k); err == nil {
69+
t.Errorf("bv error should be thrown for out of bounds access")
70+
}
71+
if _, err := abv.Get(k); err == nil {
72+
t.Errorf("abv error should be thrown for out of bounds access")
73+
}
74+
if bv.Clear(k) == nil {
75+
t.Errorf("bv error should be thrown for out of bounds access")
76+
}
4977
}
5078

5179
})
@@ -63,22 +91,22 @@ func TestBV(t *testing.T) {
6391
}
6492

6593
var r = rand.New(rand.NewSource(99))
66-
var sets = r.Perm(1024 * 1024)
94+
var sets = r.Perm(setSizes[len(setSizes)-1])
6795

6896
func BenchmarkSet(b *testing.B) {
6997
for _, setSize := range setSizes {
7098
b.Run(fmt.Sprintf("bitvec/Set size %d", setSize), func(b *testing.B) {
7199
bv := New(uint64(setSize))
72100
b.ResetTimer()
73101
for i := 0; i < b.N; i++ {
74-
bv.TrySet(uint64(sets[i%len(sets)] % setSize))
102+
bv.Set(uint64(sets[i%len(sets)] % setSize))
75103
}
76104
})
77105
b.Run(fmt.Sprintf("abitvec/Set size %d", setSize), func(b *testing.B) {
78106
abv := NewAtomic(uint64(setSize))
79107
b.ResetTimer()
80108
for i := 0; i < b.N; i++ {
81-
abv.TrySet(uint64(sets[i%len(sets)] % setSize))
109+
abv.Set(uint64(sets[i%len(sets)] % setSize))
82110
}
83111
})
84112
b.Run(fmt.Sprintf("slice/Set size %d", setSize), func(b *testing.B) {
@@ -89,8 +117,28 @@ func BenchmarkSet(b *testing.B) {
89117
}
90118
})
91119
}
120+
fmt.Println()
92121
}
93122

123+
func BenchmarkTrySet(b *testing.B) {
124+
for _, setSize := range setSizes {
125+
b.Run(fmt.Sprintf("bitvec/TrySet size %d", setSize), func(b *testing.B) {
126+
bv := New(uint64(setSize))
127+
b.ResetTimer()
128+
for i := 0; i < b.N; i++ {
129+
bv.TrySet(uint64(sets[i%len(sets)] % setSize))
130+
}
131+
})
132+
b.Run(fmt.Sprintf("abitvec/TrySet size %d", setSize), func(b *testing.B) {
133+
abv := NewAtomic(uint64(setSize))
134+
b.ResetTimer()
135+
for i := 0; i < b.N; i++ {
136+
abv.TrySet(uint64(sets[i%len(sets)] % setSize))
137+
}
138+
})
139+
}
140+
fmt.Println()
141+
}
94142
func BenchmarkGet(b *testing.B) {
95143
for _, setSize := range setSizes {
96144
b.Run(fmt.Sprintf("bitvec/Get size %d", setSize), func(b *testing.B) {
@@ -124,4 +172,40 @@ func BenchmarkGet(b *testing.B) {
124172
}
125173
})
126174
}
175+
fmt.Println()
176+
}
177+
func BenchmarkClear(b *testing.B) {
178+
for _, setSize := range setSizes {
179+
b.Run(fmt.Sprintf("bitvec/Clear size %d", setSize), func(b *testing.B) {
180+
bv := New(uint64(setSize))
181+
for i := 0; i < b.N; i++ {
182+
bv.Set(uint64(sets[i%len(sets)] % setSize))
183+
}
184+
b.ResetTimer()
185+
for i := 0; i < b.N; i++ {
186+
bv.Clear(uint64(sets[i%len(sets)] % setSize))
187+
}
188+
})
189+
b.Run(fmt.Sprintf("abitvec/Clear size %d", setSize), func(b *testing.B) {
190+
abv := NewAtomic(uint64(setSize))
191+
for i := 0; i < b.N; i++ {
192+
abv.Set(uint64(sets[i%len(sets)] % setSize))
193+
}
194+
b.ResetTimer()
195+
for i := 0; i < b.N; i++ {
196+
abv.Clear(uint64(sets[i%len(sets)] % setSize))
197+
}
198+
})
199+
b.Run(fmt.Sprintf("slice/Clear size %d", setSize), func(b *testing.B) {
200+
slice := make([]bool, setSize)
201+
for i := 0; i < b.N; i++ {
202+
slice[sets[i%len(sets)]%setSize] = true
203+
}
204+
b.ResetTimer()
205+
for i := 0; i < b.N; i++ {
206+
slice[sets[i%len(sets)]%setSize] = false
207+
}
208+
})
209+
}
210+
fmt.Println()
127211
}

0 commit comments

Comments
 (0)