Skip to content

Commit d80e3ee

Browse files
authored
Merge pull request #22 from weaviate/improve_expand
improvement: introduce expandConditionally method
2 parents 950a2f6 + 401fcb8 commit d80e3ee

File tree

7 files changed

+584
-422
lines changed

7 files changed

+584
-422
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
- name: Set up Go
2020
uses: actions/setup-go@v3
2121
with:
22-
go-version: 1.19
22+
go-version: 1.24
2323
cache: true
2424

2525
- name: Test

bitmap.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,12 @@ func (ra *Bitmap) expandKeys(bySize uint64) uint64 {
178178
ra.scootRight(curSize, bySize)
179179
ra.keys = uint16To64SliceUnsafe(ra.data[:curSize+bySize])
180180
ra.keys.setNodeSize(int(curSize + bySize))
181-
181+
182182
// All containers have moved to the right by bySize bytes.
183183
// Update their offsets.
184184
n := ra.keys
185-
for i := 0; i < n.maxKeys(); i++ {
186-
val := n.val(i)
187-
if val > 0 {
188-
n.setAt(valOffset(i), val+uint64(bySize))
189-
}
185+
for i := 0; i < n.numKeys(); i++ {
186+
n.setAt(valOffset(i), n.val(i)+uint64(bySize))
190187
}
191188
return bySize
192189
}

bitmap_opt.go

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ func andNotContainers(a, b, res *Bitmap, optBuf []uint16) {
130130
bi, bn := 0, b.keys.numKeys()
131131

132132
akToAc := map[uint64][]uint16{}
133-
sizeContainers := uint64(0)
133+
sizeContainers := 0
134134
newKeys := 0
135135

136136
for ai < an && bi < bn {
@@ -154,7 +154,7 @@ func andNotContainers(a, b, res *Bitmap, optBuf []uint16) {
154154
ac := a.getContainer(off)
155155
if getCardinality(ac) > 0 {
156156
akToAc[ak] = ac
157-
sizeContainers += uint64(len(ac))
157+
sizeContainers += len(ac)
158158
newKeys++
159159
}
160160
ai++
@@ -168,18 +168,13 @@ func andNotContainers(a, b, res *Bitmap, optBuf []uint16) {
168168
if getCardinality(ac) > 0 {
169169
ak := a.keys.key(ai)
170170
akToAc[ak] = ac
171-
sizeContainers += uint64(len(ac))
171+
sizeContainers += len(ac)
172172
newKeys++
173173
}
174174
}
175175

176176
if sizeContainers > 0 {
177-
// 2x uint64 = 8x uint16; for key and offset; twice as much as actually needed
178-
sizeKeys := 8 * uint64(newKeys) * 2
179-
// ensure enough space for new containers and keys,
180-
// allocate required memory just once to avoid copying underlying data slice multiple times
181-
res.expandNoLengthChange(sizeContainers + sizeKeys)
182-
res.expandKeys(sizeKeys)
177+
res.expandConditionally(newKeys, sizeContainers)
183178

184179
for ak, ac := range akToAc {
185180
// create a new container and update the key offset to this container.
@@ -290,7 +285,7 @@ func orContainers(a, b, res *Bitmap, buf []uint16) {
290285

291286
akToAc := map[uint64][]uint16{}
292287
bkToBc := map[uint64][]uint16{}
293-
sizeContainers := uint64(0)
288+
sizeContainers := 0
294289
newKeys := 0
295290

296291
for ai < an && bi < bn {
@@ -318,7 +313,7 @@ func orContainers(a, b, res *Bitmap, buf []uint16) {
318313
ac := a.getContainer(off)
319314
if getCardinality(ac) > 0 {
320315
akToAc[ak] = ac
321-
sizeContainers += uint64(len(ac))
316+
sizeContainers += len(ac)
322317
newKeys++
323318
}
324319
ai++
@@ -327,7 +322,7 @@ func orContainers(a, b, res *Bitmap, buf []uint16) {
327322
bc := b.getContainer(off)
328323
if getCardinality(bc) > 0 {
329324
bkToBc[bk] = bc
330-
sizeContainers += uint64(len(bc))
325+
sizeContainers += len(bc)
331326
newKeys++
332327
}
333328
bi++
@@ -339,7 +334,7 @@ func orContainers(a, b, res *Bitmap, buf []uint16) {
339334
if getCardinality(ac) > 0 {
340335
ak := a.keys.key(ai)
341336
akToAc[ak] = ac
342-
sizeContainers += uint64(len(ac))
337+
sizeContainers += len(ac)
343338
newKeys++
344339
}
345340
}
@@ -349,18 +344,13 @@ func orContainers(a, b, res *Bitmap, buf []uint16) {
349344
if getCardinality(bc) > 0 {
350345
bk := b.keys.key(bi)
351346
bkToBc[bk] = bc
352-
sizeContainers += uint64(len(bc))
347+
sizeContainers += len(bc)
353348
newKeys++
354349
}
355350
}
356351

357352
if sizeContainers > 0 {
358-
// 2x uint64 = 8x uint16; for key and offset; twice as much as actually needed
359-
sizeKeys := 8 * uint64(newKeys) * 2
360-
// ensure enough space for new containers and keys,
361-
// allocate required memory just once avoid copying underlying data slice multiple times
362-
res.expandNoLengthChange(sizeContainers + sizeKeys)
363-
res.expandKeys(sizeKeys)
353+
res.expandConditionally(newKeys, sizeContainers)
364354

365355
for ak, ac := range akToAc {
366356
// create a new container and update the key offset to this container.
@@ -395,7 +385,7 @@ func orContainersInRange(a, b *Bitmap, bi, bn int, buf []uint16) {
395385
// copy containers from b to a all at once
396386
// expanding underlying data slice and keys subslice once
397387
bkToBc := map[uint64][]uint16{}
398-
sizeContainers := uint64(0)
388+
sizeContainers := 0
399389
newKeys := 0
400390

401391
for ai < an && bi < bn {
@@ -435,7 +425,7 @@ func orContainersInRange(a, b *Bitmap, bi, bn int, buf []uint16) {
435425
bc := b.getContainer(off)
436426
if getCardinality(bc) > 0 {
437427
bkToBc[bk] = bc
438-
sizeContainers += uint64(len(bc))
428+
sizeContainers += len(bc)
439429
newKeys++
440430
}
441431
bi++
@@ -447,23 +437,13 @@ func orContainersInRange(a, b *Bitmap, bi, bn int, buf []uint16) {
447437
if getCardinality(bc) > 0 {
448438
bk := b.keys.key(bi)
449439
bkToBc[bk] = bc
450-
sizeContainers += uint64(len(bc))
440+
sizeContainers += len(bc)
451441
newKeys++
452442
}
453443
}
454444

455445
if sizeContainers > 0 {
456-
if a.keysExpansionRequired(newKeys) {
457-
// 2x uint64 = 8x uint16; for key and offset; twice as much as actually needed
458-
sizeKeys := 8 * uint64(newKeys) * 2
459-
460-
// ensure enough space for new containers and keys,
461-
// allocate required memory just once to avoid copying underlying data slice multiple times
462-
a.expandNoLengthChange(sizeContainers + sizeKeys)
463-
a.expandKeys(sizeKeys)
464-
} else {
465-
a.expandNoLengthChange(sizeContainers)
466-
}
446+
a.expandConditionally(newKeys, sizeContainers)
467447

468448
for bk, bc := range bkToBc {
469449
// create a new container and update the key offset to this container.
@@ -498,36 +478,26 @@ func (ra *Bitmap) OrConc(bm *Bitmap, maxConcurrency int) *Bitmap {
498478
return ra
499479
}
500480

501-
var totalSizeContainers uint64
502481
var totalNewKeys int
482+
var totalSizeContainers int
503483
var allKeys []uint64
504484
var allContainers [][]uint16
505485
lock := new(sync.Mutex)
506486
inlineVsMutateLock := new(sync.RWMutex)
507487
callback := func(bi, bj, _ int) {
508488
buf := make([]uint16, maxContainerSize)
509-
sizeContainers, newKeys, keys, containers := orContainersInRangeConc(ra, bm, bi, bj, buf, inlineVsMutateLock)
489+
newKeys, sizeContainers, keys, containers := orContainersInRangeConc(ra, bm, bi, bj, buf, inlineVsMutateLock)
510490

511491
lock.Lock()
512-
totalSizeContainers += sizeContainers
513492
totalNewKeys += newKeys
493+
totalSizeContainers += sizeContainers
514494
allKeys = append(allKeys, keys...)
515495
allContainers = append(allContainers, containers...)
516496
lock.Unlock()
517497
}
518498
concurrentlyInRanges(numContainers, concurrency, callback)
519499
if totalSizeContainers > 0 {
520-
if ra.keysExpansionRequired(totalNewKeys) {
521-
// 2x uint64 = 8x uint16; for key and offset; twice as much as actually needed
522-
totalSizeKeys := 8 * uint64(totalNewKeys) * 2
523-
524-
// ensure enough space for new containers and keys,
525-
// allocate required memory just once to avoid copying underlying data slice multiple times
526-
ra.expandNoLengthChange(totalSizeContainers + totalSizeKeys)
527-
ra.expandKeys(totalSizeKeys)
528-
} else {
529-
ra.expandNoLengthChange(totalSizeContainers)
530-
}
500+
ra.expandConditionally(totalNewKeys, totalSizeContainers)
531501

532502
for i, container := range allContainers {
533503
// create a new container and update the key offset to this container.
@@ -541,7 +511,7 @@ func (ra *Bitmap) OrConc(bm *Bitmap, maxConcurrency int) *Bitmap {
541511
}
542512

543513
func orContainersInRangeConc(a, b *Bitmap, bi, bn int, buf []uint16, inlineVsMutateLock *sync.RWMutex,
544-
) (sizeContainers uint64, newKeys int, bKeys []uint64, bContainers [][]uint16) {
514+
) (newKeys, sizeContainers int, bKeys []uint64, bContainers [][]uint16) {
545515
bk := b.keys.key(bi)
546516
ai := a.keys.search(bk)
547517
an := a.keys.numKeys()
@@ -597,7 +567,7 @@ func orContainersInRangeConc(a, b *Bitmap, bi, bn int, buf []uint16, inlineVsMut
597567
if getCardinality(bc) > 0 {
598568
bKeys = append(bKeys, bk)
599569
bContainers = append(bContainers, bc)
600-
sizeContainers += uint64(len(bc))
570+
sizeContainers += len(bc)
601571
newKeys++
602572
}
603573
bi++
@@ -610,7 +580,7 @@ func orContainersInRangeConc(a, b *Bitmap, bi, bn int, buf []uint16, inlineVsMut
610580
bk := b.keys.key(bi)
611581
bKeys = append(bKeys, bk)
612582
bContainers = append(bContainers, bc)
613-
sizeContainers += uint64(len(bc))
583+
sizeContainers += len(bc)
614584
newKeys++
615585
}
616586
}
@@ -914,15 +884,7 @@ func (ra *Bitmap) FillUp(maxX uint64) {
914884
}
915885

916886
// calculate required memory to allocate and expand underlying slice
917-
containersLen := uint64(requiredContainersCount * maxContainerSize)
918-
if ra.keysExpansionRequired(requiredContainersCount) {
919-
// 2x uint64 = 8x uint16; for key and offset; twice as much as actually needed
920-
keysLen := uint64(requiredContainersCount*2*4) * 2
921-
ra.expandNoLengthChange(containersLen + keysLen)
922-
ra.expandKeys(keysLen)
923-
} else {
924-
ra.expandNoLengthChange(containersLen)
925-
}
887+
ra.expandConditionally(requiredContainersCount, requiredContainersCount*maxContainerSize)
926888

927889
var onesBitmap bitmap
928890
if containerIdx < maxContainersCount {
@@ -1069,6 +1031,65 @@ func (b bitmap) fillWithOnes() {
10691031
}
10701032
}
10711033

1072-
func (ra *Bitmap) keysExpansionRequired(newKeys int) bool {
1073-
return ra.keys.numKeys()+newKeys >= ra.keys.maxKeys()
1034+
func (ra *Bitmap) expandConditionally(newKeys int, sizeContainers int) {
1035+
cp := cap(ra.data)
1036+
ln := len(ra.data)
1037+
1038+
sizeKeys := 8 * newKeys // 2x uint64 (key+offset) = 8x uint16
1039+
if ra.keys.numKeys()+newKeys < ra.keys.maxKeys() {
1040+
if ln+sizeContainers <= cp {
1041+
// keys and containers fit. nothing to do
1042+
return
1043+
}
1044+
// keys fit, containers do not. expand slice to make room for containers and only new keys
1045+
} else {
1046+
if ln+sizeKeys+sizeContainers <= cp {
1047+
curNumKeys := ra.keys.numKeys()
1048+
1049+
// keys do not fit, containers do. just move containers to make room for keys
1050+
if curNumKeys > newKeys {
1051+
// make room for up to curNumKeys additional keys
1052+
sizeKeys = 8 * curNumKeys // 2x uint64 (key+offset) = 8x uint16
1053+
if left := cp - ln - sizeContainers; left < sizeKeys {
1054+
sizeKeys = left / 8 * 8
1055+
}
1056+
}
1057+
1058+
// new containers will fit. just move containers to make room for keys
1059+
curSizeKeys := ra.keys.size()
1060+
newSizeKeys := curSizeKeys + sizeKeys
1061+
1062+
ra.data = ra.data[:ln+sizeKeys]
1063+
n := copy(ra.data[newSizeKeys:], ra.data[curSizeKeys:])
1064+
ra.memMoved += n
1065+
Memclr(ra.data[curSizeKeys:newSizeKeys]) // Zero out the space in the middle.
1066+
1067+
ra.keys = uint16To64SliceUnsafe(ra.data[:newSizeKeys])
1068+
ra.keys.setNodeSize(newSizeKeys)
1069+
for i := 0; i < curNumKeys; i++ {
1070+
ra.keys.setAt(valOffset(i), ra.keys.val(i)+uint64(sizeKeys))
1071+
}
1072+
return
1073+
}
1074+
// neither keys nor containers fit. expand slice to make room for containers and more keys
1075+
sizeKeys = 8 * max(newKeys, ra.keys.numKeys()) // 2x uint64 (key+offset) = 8x uint16
1076+
}
1077+
1078+
// expand 2x (or up to sizeKeys+sizeNewContainers if 2x is too little)
1079+
growBy := max(cp, sizeKeys+sizeContainers)
1080+
out := make([]uint16, ln+sizeKeys, cp+growBy)
1081+
1082+
curSizeKeys := ra.keys.size()
1083+
newSizeKeys := curSizeKeys + sizeKeys
1084+
copy(out, ra.data[:curSizeKeys])
1085+
copy(out[newSizeKeys:], ra.data[curSizeKeys:])
1086+
ra.data = out
1087+
ra._ptr = nil // Allow Go to GC whatever this was pointing to.
1088+
// Re-reference ra.keys correctly because underlying array has changed.
1089+
1090+
ra.keys = uint16To64SliceUnsafe(ra.data[:newSizeKeys])
1091+
ra.keys.setNodeSize(newSizeKeys)
1092+
for i := 0; i < ra.keys.numKeys(); i++ {
1093+
ra.keys.setAt(valOffset(i), ra.keys.val(i)+uint64(sizeKeys))
1094+
}
10741095
}

0 commit comments

Comments
 (0)