Skip to content

Commit 5f537c4

Browse files
authored
Rollup merge of #133185 - notriddle:notriddle/roaringbitmap, r=notriddle
rustdoc-search: use smart binary search in bitmaps Addresses a comment from [jsha's benchmarking], where the `contains` function showed up in the profiler. This commit pulls it from about 5% of the runtime to about 0.5%. Before: https://share.firefox.dev/3ANVjon After: https://share.firefox.dev/3OeM3gk [jsha's benchmarking]: https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/search.20profiling/near/481868761
2 parents 17ffefc + 826d023 commit 5f537c4

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

src/librustdoc/html/static/js/search.js

+55-10
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,12 @@ class VlqHexDecoder {
988988
}
989989
class RoaringBitmap {
990990
constructor(str) {
991+
// https://github.com/RoaringBitmap/RoaringFormatSpec
992+
//
993+
// Roaring bitmaps are used for flags that can be kept in their
994+
// compressed form, even when loaded into memory. This decoder
995+
// turns the containers into objects, but uses byte array
996+
// slices of the original format for the data payload.
991997
const strdecoded = atob(str);
992998
const u8array = new Uint8Array(strdecoded.length);
993999
for (let j = 0; j < strdecoded.length; ++j) {
@@ -1053,9 +1059,24 @@ class RoaringBitmap {
10531059
contains(keyvalue) {
10541060
const key = keyvalue >> 16;
10551061
const value = keyvalue & 0xFFFF;
1056-
for (let i = 0; i < this.keys.length; ++i) {
1057-
if (this.keys[i] === key) {
1058-
return this.containers[i].contains(value);
1062+
// Binary search algorithm copied from
1063+
// https://en.wikipedia.org/wiki/Binary_search#Procedure
1064+
//
1065+
// Format is required by specification to be sorted.
1066+
// Because keys are 16 bits and unique, length can't be
1067+
// bigger than 2**16, and because we have 32 bits of safe int,
1068+
// left + right can't overflow.
1069+
let left = 0;
1070+
let right = this.keys.length - 1;
1071+
while (left <= right) {
1072+
const mid = Math.floor((left + right) / 2);
1073+
const x = this.keys[mid];
1074+
if (x < key) {
1075+
left = mid + 1;
1076+
} else if (x > key) {
1077+
right = mid - 1;
1078+
} else {
1079+
return this.containers[mid].contains(value);
10591080
}
10601081
}
10611082
return false;
@@ -1068,11 +1089,23 @@ class RoaringBitmapRun {
10681089
this.array = array;
10691090
}
10701091
contains(value) {
1071-
const l = this.runcount * 4;
1072-
for (let i = 0; i < l; i += 4) {
1092+
// Binary search algorithm copied from
1093+
// https://en.wikipedia.org/wiki/Binary_search#Procedure
1094+
//
1095+
// Since runcount is stored as 16 bits, left + right
1096+
// can't overflow.
1097+
let left = 0;
1098+
let right = this.runcount - 1;
1099+
while (left <= right) {
1100+
const mid = Math.floor((left + right) / 2);
1101+
const i = mid * 4;
10731102
const start = this.array[i] | (this.array[i + 1] << 8);
10741103
const lenm1 = this.array[i + 2] | (this.array[i + 3] << 8);
1075-
if (value >= start && value <= (start + lenm1)) {
1104+
if ((start + lenm1) < value) {
1105+
left = mid + 1;
1106+
} else if (start > value) {
1107+
right = mid - 1;
1108+
} else {
10761109
return true;
10771110
}
10781111
}
@@ -1085,10 +1118,22 @@ class RoaringBitmapArray {
10851118
this.array = array;
10861119
}
10871120
contains(value) {
1088-
const l = this.cardinality * 2;
1089-
for (let i = 0; i < l; i += 2) {
1090-
const start = this.array[i] | (this.array[i + 1] << 8);
1091-
if (value === start) {
1121+
// Binary search algorithm copied from
1122+
// https://en.wikipedia.org/wiki/Binary_search#Procedure
1123+
//
1124+
// Since cardinality can't be higher than 4096, left + right
1125+
// cannot overflow.
1126+
let left = 0;
1127+
let right = this.cardinality - 1;
1128+
while (left <= right) {
1129+
const mid = Math.floor((left + right) / 2);
1130+
const i = mid * 2;
1131+
const x = this.array[i] | (this.array[i + 1] << 8);
1132+
if (x < value) {
1133+
left = mid + 1;
1134+
} else if (x > value) {
1135+
right = mid - 1;
1136+
} else {
10921137
return true;
10931138
}
10941139
}

0 commit comments

Comments
 (0)