Skip to content

Commit c24ec5f

Browse files
committed
optimize bitmap
1 parent c474238 commit c24ec5f

File tree

3 files changed

+104
-57
lines changed

3 files changed

+104
-57
lines changed

structs/bitmap/bitmap.go

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,59 +11,66 @@ import (
1111

1212
const (
1313
blockSize = 8
14+
MaxIndex = uint64(1 << 34)
1415
)
1516

1617
type Bitmap struct {
17-
data []byte
18-
size uint64
18+
bits []byte
19+
20+
blocks uint64
1921
max uint64
20-
mux sync.RWMutex
2122
lockoff bool
23+
24+
mux sync.RWMutex
2225
}
2326

2427
type Option func(*Bitmap)
2528

26-
func DisableLock() Option {
29+
func OptDisableLock() Option {
2730
return func(o *Bitmap) {
2831
o.lockoff = true
2932
}
3033
}
3134

32-
func New(maxIndex uint64, opts ...Option) *Bitmap {
33-
size := maxIndex/blockSize + maxIndex%blockSize
34-
35-
bm := &Bitmap{
36-
max: size * blockSize,
37-
size: size,
38-
data: make([]byte, size),
35+
func OptMaxIndex(index uint64) Option {
36+
return func(o *Bitmap) {
37+
o.max = index
3938
}
39+
}
40+
41+
func New(opts ...Option) *Bitmap {
42+
bm := &Bitmap{max: 1}
4043

4144
for _, opt := range opts {
4245
opt(bm)
4346
}
4447

48+
bm.resize(bm.max)
49+
4550
return bm
4651
}
4752

48-
func (b *Bitmap) CopyTo(dst *Bitmap) {
49-
if !b.lockoff {
50-
b.mux.Lock()
51-
defer b.mux.Unlock()
52-
}
53-
if !dst.lockoff {
54-
dst.mux.Lock()
55-
defer dst.mux.Unlock()
53+
func (b *Bitmap) resize(index uint64) {
54+
size := index / blockSize
55+
if index-(size%blockSize) > 0 {
56+
size++
5657
}
5758

58-
dst.data = make([]byte, len(b.data))
59-
copy(dst.data, b.data)
60-
dst.size = b.size
61-
dst.max = b.max
62-
dst.lockoff = b.lockoff
59+
b.bits = append(b.bits, make([]byte, size-b.blocks)...)
60+
b.blocks = uint64(len(b.bits))
61+
b.max = b.blocks*blockSize - 1
62+
}
63+
64+
func (b *Bitmap) getBlock(index uint64) uint64 {
65+
return index / blockSize
66+
}
67+
68+
func (b *Bitmap) getBit(index uint64) byte {
69+
return 1 << (index - b.getBlock(index)*blockSize)
6370
}
6471

6572
func (b *Bitmap) Set(index uint64) {
66-
if index > b.max {
73+
if index > MaxIndex {
6774
return
6875
}
6976

@@ -72,11 +79,15 @@ func (b *Bitmap) Set(index uint64) {
7279
defer b.mux.Unlock()
7380
}
7481

75-
b.data[index%b.size] |= 1 << (index % blockSize)
82+
if index > b.max {
83+
b.resize(index)
84+
}
85+
86+
b.bits[b.getBlock(index)] |= b.getBit(index)
7687
}
7788

7889
func (b *Bitmap) Del(index uint64) {
79-
if index > b.max {
90+
if index > b.max || index > MaxIndex {
8091
return
8192
}
8293

@@ -85,11 +96,11 @@ func (b *Bitmap) Del(index uint64) {
8596
defer b.mux.Unlock()
8697
}
8798

88-
b.data[index%b.size] &^= 1 << (index % blockSize)
99+
b.bits[b.getBlock(index)] &^= b.getBit(index)
89100
}
90101

91102
func (b *Bitmap) Has(index uint64) bool {
92-
if index > b.max {
103+
if index > b.max || index > MaxIndex {
93104
return false
94105
}
95106

@@ -98,29 +109,49 @@ func (b *Bitmap) Has(index uint64) bool {
98109
defer b.mux.RUnlock()
99110
}
100111

101-
return (b.data[index%b.size] & (1 << (index % blockSize))) > 0
112+
return (b.bits[b.getBlock(index)] & b.getBit(index)) > 0
102113
}
103114

104-
func (b *Bitmap) Dump() []byte {
115+
func (b *Bitmap) MarshalBinary() ([]byte, error) {
105116
if !b.lockoff {
106117
b.mux.RLock()
107118
defer b.mux.RUnlock()
108119
}
109120

110-
out := make([]byte, b.size)
111-
copy(out, b.data)
112-
return out
121+
out := make([]byte, b.blocks)
122+
copy(out, b.bits)
123+
124+
return out, nil
113125
}
114126

115-
func (b *Bitmap) Restore(in []byte) {
127+
func (b *Bitmap) UnmarshalBinary(in []byte) error {
116128
if !b.lockoff {
117129
b.mux.Lock()
118130
defer b.mux.Unlock()
119131
}
120132

121-
b.data = make([]byte, len(in))
122-
copy(b.data, in)
133+
b.bits = make([]byte, len(in))
134+
copy(b.bits, in)
123135

124-
b.size = uint64(len(in))
125-
b.max = b.size * blockSize
136+
b.blocks = uint64(len(in))
137+
b.max = b.blocks * blockSize
138+
139+
return nil
140+
}
141+
142+
func (b *Bitmap) CopyTo(dst *Bitmap) {
143+
if !b.lockoff {
144+
b.mux.Lock()
145+
defer b.mux.Unlock()
146+
}
147+
if !dst.lockoff {
148+
dst.mux.Lock()
149+
defer dst.mux.Unlock()
150+
}
151+
152+
dst.bits = make([]byte, len(b.bits))
153+
copy(dst.bits, b.bits)
154+
dst.blocks = b.blocks
155+
dst.max = b.max
156+
dst.lockoff = b.lockoff
126157
}

structs/bitmap/bitmap_test.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,41 @@
66
package bitmap
77

88
import (
9-
"math"
9+
"fmt"
1010
"testing"
1111

1212
"go.osspkg.com/casecheck"
1313
)
1414

1515
func TestUnit_Bitmap_CopyTO(t *testing.T) {
16-
src := New(65)
16+
src := New()
1717
src.Set(1)
1818
src.Set(5)
1919
src.Set(60)
2020

21-
dst := New(100)
21+
dst := New()
2222
src.CopyTo(dst)
2323

24-
casecheck.Equal(t, src.size, dst.size)
25-
casecheck.Equal(t, src.data, dst.data)
24+
casecheck.Equal(t, src.blocks, dst.blocks)
25+
casecheck.Equal(t, src.bits, dst.bits)
2626
casecheck.Equal(t, src.lockoff, dst.lockoff)
2727
casecheck.Equal(t, src.max, dst.max)
2828
}
2929

30-
func TestUnit_Bitmap_calcBlockIndex(t *testing.T) {
31-
bm := New(65)
30+
func TestUnit_Bitmap_Resize(t *testing.T) {
31+
t.Skip("Only for debug")
32+
33+
src := New()
34+
35+
for i := 0; i <= 20; i++ {
36+
src.Set(uint64(i))
37+
b, _ := src.MarshalBinary()
38+
fmt.Printf("(%d) %b `%s`\n", i, b, string(b))
39+
}
40+
}
41+
42+
func TestUnit_Bitmap_Marshaling(t *testing.T) {
43+
bm := New()
3244

3345
for i := 0; i <= 65; i++ {
3446
bm.Set(uint64(i))
@@ -37,16 +49,16 @@ func TestUnit_Bitmap_calcBlockIndex(t *testing.T) {
3749
casecheck.False(t, bm.Has(uint64(i+1)), "(2) for index: %d", i+1)
3850
}
3951

40-
backup := bm.Dump()
52+
backup, _ := bm.MarshalBinary()
4153

42-
bm.Restore(make([]byte, len(backup)))
54+
bm.UnmarshalBinary(make([]byte, len(backup)))
4355

4456
for i := 0; i <= 65; i++ {
4557
casecheck.False(t, bm.Has(uint64(i)), "(3) for index: %d", i)
4658
casecheck.False(t, bm.Has(uint64(i+1)), "(4) for index: %d", i+1)
4759
}
4860

49-
bm.Restore(backup)
61+
bm.UnmarshalBinary(backup)
5062

5163
for i := 65; i >= 0; i-- {
5264
casecheck.True(t, bm.Has(uint64(i)), "(1) for index: %d", i)
@@ -64,11 +76,12 @@ goarch: amd64
6476
pkg: go.osspkg.com/algorithms/structs/bitmap
6577
cpu: 12th Gen Intel(R) Core(TM) i9-12900KF
6678
Benchmark_Bitmap
67-
Benchmark_Bitmap-4 9942369 162.7 ns/op 0 B/op 0 allocs/op
79+
Benchmark_Bitmap-4 9515085 182.2 ns/op 225 B/op 0 allocs/op
6880
*/
6981
func Benchmark_Bitmap(b *testing.B) {
70-
index := uint64(math.MaxInt16)
71-
bm := New(index)
82+
index := uint64(1 << 34)
83+
84+
bm := New(OptMaxIndex(1024))
7285

7386
b.ReportAllocs()
7487
b.ResetTimer()

structs/bloom/bloom.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func New(opts ...Option) (*Bloom, error) {
8585
m, k := calcOptimalParams(b.optSize, b.optRate)
8686

8787
b.size = m
88-
b.bits = bitmap.New(m, bitmap.DisableLock())
88+
b.bits = bitmap.New(bitmap.OptMaxIndex(m), bitmap.OptDisableLock())
8989
b.salts = make([][saltSize]byte, k)
9090

9191
for i := 0; i < int(k); i++ {
@@ -138,7 +138,12 @@ func (b *Bloom) Dump(w io.Writer) error {
138138
}
139139
}
140140

141-
if _, err := w.Write(b.bits.Dump()); err != nil {
141+
bb, err := b.bits.MarshalBinary()
142+
if err != nil {
143+
return fmt.Errorf("marshal bitset: %w", err)
144+
}
145+
146+
if _, err = w.Write(bb); err != nil {
142147
return fmt.Errorf("write bitmap: %w", err)
143148
}
144149

@@ -194,9 +199,7 @@ func (b *Bloom) Restore(r io.Reader) error {
194199
return fmt.Errorf("read bitmap: %w", err)
195200
}
196201

197-
b.bits.Restore(bm)
198-
199-
return nil
202+
return b.bits.UnmarshalBinary(bm)
200203
}
201204

202205
func (b *Bloom) Add(arg any) {

0 commit comments

Comments
 (0)