Skip to content

Commit f7394b3

Browse files
authored
Reuse IndexedScore instances, as emptying the values is faster than discarding and re-allocating memory. (#8297)
1 parent a9ca7d6 commit f7394b3

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

app/lib/search/mem_index.dart

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class InMemoryPackageIndex {
2727
late final TokenIndex<String> _descrIndex;
2828
late final TokenIndex<String> _readmeIndex;
2929
late final TokenIndex<IndexedApiDocPage> _apiSymbolIndex;
30+
late final _scorePool = ScorePool(_packageNameIndex._packageNames);
3031

3132
/// Adjusted score takes the overall score and transforms
3233
/// it linearly into the [0.4-1.0] range.
@@ -116,8 +117,16 @@ class InMemoryPackageIndex {
116117
}
117118

118119
PackageSearchResult search(ServiceSearchQuery query) {
119-
final packageScores = IndexedScore(_packageNameIndex._packageNames, 1.0);
120+
return _scorePool.withScore(
121+
value: 1.0,
122+
fn: (score) {
123+
return _search(query, score);
124+
},
125+
);
126+
}
120127

128+
PackageSearchResult _search(
129+
ServiceSearchQuery query, IndexedScore<String> packageScores) {
121130
// filter on package prefix
122131
if (query.parsedQuery.packagePrefix != null) {
123132
final String prefix = query.parsedQuery.packagePrefix!.toLowerCase();
@@ -308,11 +317,17 @@ class InMemoryPackageIndex {
308317
nameMatches.add(word);
309318
}
310319

311-
final wordScore =
312-
_packageNameIndex.searchWord(word, filterOnNonZeros: packageScores);
313-
_descrIndex.searchAndAccumulate(word, score: wordScore);
314-
_readmeIndex.searchAndAccumulate(word, weight: 0.75, score: wordScore);
315-
packageScores.multiplyAllFrom(wordScore);
320+
_scorePool.withScore(
321+
value: 0.0,
322+
fn: (wordScore) {
323+
_packageNameIndex.searchWord(word,
324+
score: wordScore, filterOnNonZeros: packageScores);
325+
_descrIndex.searchAndAccumulate(word, score: wordScore);
326+
_readmeIndex.searchAndAccumulate(word,
327+
weight: 0.75, score: wordScore);
328+
packageScores.multiplyAllFrom(wordScore);
329+
},
330+
);
316331
}
317332

318333
final topApiPages =
@@ -483,7 +498,8 @@ class PackageNameIndex {
483498
Map<String, double> search(String text) {
484499
IndexedScore<String>? score;
485500
for (final w in splitForQuery(text)) {
486-
final s = searchWord(w, filterOnNonZeros: score);
501+
final s = IndexedScore(_packageNames);
502+
searchWord(w, score: s, filterOnNonZeros: score);
487503
if (score == null) {
488504
score = s;
489505
} else {
@@ -498,11 +514,12 @@ class PackageNameIndex {
498514
///
499515
/// When [filterOnNonZeros] is present, only the indexes with an already
500516
/// non-zero value are evaluated.
501-
IndexedScore<String> searchWord(
517+
void searchWord(
502518
String word, {
519+
required IndexedScore<String> score,
503520
IndexedScore<String>? filterOnNonZeros,
504521
}) {
505-
final score = IndexedScore(_packageNames);
522+
assert(score.keys.length == _packageNames.length);
506523
final singularWord = word.length <= 3 || !word.endsWith('s')
507524
? word
508525
: word.substring(0, word.length - 1);
@@ -543,7 +560,6 @@ class PackageNameIndex {
543560
}
544561
}
545562
}
546-
return score;
547563
}
548564
}
549565

app/lib/search/token_index.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class TokenIndex<K> {
100100
/// scoring.
101101
IndexedScore<K> searchWords(List<String> words, {double weight = 1.0}) {
102102
IndexedScore<K>? score;
103+
103104
weight = math.pow(weight, 1 / words.length).toDouble();
104105
for (final w in words) {
105106
final s = IndexedScore(_ids);
@@ -142,6 +143,30 @@ extension StringTokenIndexExt on TokenIndex<String> {
142143
}
143144
}
144145

146+
/// A reusable pool for [IndexedScore] instances to spare some memory allocation.
147+
class ScorePool<K> {
148+
final List<K> _keys;
149+
final _pool = <IndexedScore<K>>[];
150+
151+
ScorePool(this._keys);
152+
153+
R withScore<R>({
154+
required double value,
155+
required R Function(IndexedScore<K> score) fn,
156+
}) {
157+
late IndexedScore<K> score;
158+
if (_pool.isNotEmpty) {
159+
score = _pool.removeLast();
160+
score._values.setAll(0, Iterable.generate(score.length, (_) => value));
161+
} else {
162+
score = IndexedScore<K>(_keys, value);
163+
}
164+
final r = fn(score);
165+
_pool.add(score);
166+
return r;
167+
}
168+
}
169+
145170
/// Mutable score list that can accessed via integer index.
146171
class IndexedScore<K> {
147172
final List<K> _keys;

0 commit comments

Comments
 (0)