Skip to content
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
42 changes: 42 additions & 0 deletions benchmark_opt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,45 @@ func BenchmarkCompactedFirstInsert(b *testing.B) {
})
}
}

// go test -bench BenchmarkGrownCap -run ^$ github.com/weaviate/sroar
func BenchmarkGrownCap(b *testing.B) {
// The trace appends one sparse container and its key entry at a time, so peak
// slack and copy work can be read off against the pair that produced them.
const step = minContainerSize + 8

for _, limit := range []int{512 * 1024, growDoubleLimit, 4 * growDoubleLimit} {
for _, div := range []int{2, growSlackDiv, 8} {
for _, finalSize := range []int{1 << 22, 1 << 24, 1 << 25} {
if finalSize < 2*limit {
// The trace never leaves the doubling arm, so the pair is unswept.
continue
}
name := "limit_" + strconv.Itoa(limit) + "/div_" + strconv.Itoa(div) +
"/final_" + strconv.Itoa(finalSize)
b.Run(name, func(b *testing.B) {
var ln, copied, peakSlack int
buf := []uint16(nil)
for i := 0; i < b.N; i++ {
ln, copied, peakSlack = 0, 0, 0
buf = nil
for ln < finalSize {
if ln+step > cap(buf) {
grown := make([]uint16, ln, grownCapWith(cap(buf), step, limit, div))
copied += copy(grown, buf)
buf = grown
if s := cap(buf) - (ln + step); s > peakSlack {
peakSlack = s
}
}
ln += step
buf = buf[:ln]
}
}
b.ReportMetric(float64(peakSlack)*2, "peak-slack-bytes")
b.ReportMetric(float64(copied)*2, "bytes-copied")
})
}
}
}
}
30 changes: 25 additions & 5 deletions bitmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,7 @@ func (ra *Bitmap) expandNoLengthChange(bySize uint64) (toSize int) {
if toSize <= cap(ra.data) {
return
}
growBy := cap(ra.data)
if growBy < int(bySize) {
growBy = int(bySize)
}
out := make([]uint16, len(ra.data), cap(ra.data)+growBy)
out := make([]uint16, len(ra.data), grownCap(cap(ra.data), int(bySize)))
copy(out, ra.data)
prev := len(ra.keys) * 4 // Multiply by 4 to convert from u16 to u64.
ra.data = out
Expand All @@ -247,6 +243,30 @@ func (ra *Bitmap) expandNoLengthChange(bySize uint64) (toSize int) {
return
}

// growDoubleLimit is the buffer size past which grownCap stops doubling, so a
// large bitmap does not carry a copy of itself in slack for the life of the
// process. BenchmarkGrownCap sweeps it against a monotone-append trace.
const growDoubleLimit = 4096 * 1024 // In Uint16.

// growSlackDiv divides the grown size to give grownCap's slack past that limit.
// A smaller divisor copies less and leaves more slack, and the same benchmark sweeps it.
const growSlackDiv = 4

func grownCap(cp, need int) int {
return grownCapWith(cp, need, growDoubleLimit, growSlackDiv)
}

// grownCapWith doubles while cp stays under limit, which leaves an exact fit when
// need exceeds cp. Past the limit the slack is the grown size over div, so a buffer
// just under the limit grows larger than one just over it.
func grownCapWith(cp, need, limit, div int) int {
want := cp + need
if cp < limit {
return max(cp+cp, want)
}
return want + want/div
}

// scootRight isn't aware of containers. It's going to create empty space of
// bySize at the given offset in ra.data. The offset doesn't need to line up
// with a container.
Expand Down
8 changes: 4 additions & 4 deletions bitmap_opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,9 @@ func (ra *Bitmap) LenInBytes() int {
return len(ra.data) * 2
}

func (ra *Bitmap) capInBytes() int {
// CapInBytes returns the size of the allocated buffer, including the growth
// slack that LenInBytes does not count.
func (ra *Bitmap) CapInBytes() int {
if ra == nil {
return 0
}
Expand Down Expand Up @@ -1712,9 +1714,7 @@ func (ra *Bitmap) expandConditionally(newKeys int, sizeContainers int) {
sizeKeys = 8 * max(newKeys, curNumKeys) // 2x uint64 (key+offset) = 8x uint16
}

// expand 2x (or up to sizeKeys+sizeNewContainers if 2x is too little)
growBy := max(cp, sizeKeys+sizeContainers)
out := make([]uint16, ln+sizeKeys, cp+growBy)
out := make([]uint16, ln+sizeKeys, grownCap(cp, sizeKeys+sizeContainers))

newSizeKeys := curSizeKeys + sizeKeys
copy(out, ra.data[:curSizeKeys])
Expand Down
Loading
Loading