From ed33a82debec2a3197c17c0f60513996fac2b7d6 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Mon, 18 Nov 2024 12:32:17 +0100 Subject: [PATCH] Reuse IndexedScore instances, as emptying the values is faster than discarding and re-allocating memory. --- app/lib/search/mem_index.dart | 36 ++++++++++++++++++++++++--------- app/lib/search/token_index.dart | 25 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/app/lib/search/mem_index.dart b/app/lib/search/mem_index.dart index b8d312634e..31538810b4 100644 --- a/app/lib/search/mem_index.dart +++ b/app/lib/search/mem_index.dart @@ -27,6 +27,7 @@ class InMemoryPackageIndex { late final TokenIndex _descrIndex; late final TokenIndex _readmeIndex; late final TokenIndex _apiSymbolIndex; + late final _scorePool = ScorePool(_packageNameIndex._packageNames); /// Adjusted score takes the overall score and transforms /// it linearly into the [0.4-1.0] range. @@ -116,8 +117,16 @@ class InMemoryPackageIndex { } PackageSearchResult search(ServiceSearchQuery query) { - final packageScores = IndexedScore(_packageNameIndex._packageNames, 1.0); + return _scorePool.withScore( + value: 1.0, + fn: (score) { + return _search(query, score); + }, + ); + } + PackageSearchResult _search( + ServiceSearchQuery query, IndexedScore packageScores) { // filter on package prefix if (query.parsedQuery.packagePrefix != null) { final String prefix = query.parsedQuery.packagePrefix!.toLowerCase(); @@ -308,11 +317,17 @@ class InMemoryPackageIndex { nameMatches.add(word); } - final wordScore = - _packageNameIndex.searchWord(word, filterOnNonZeros: packageScores); - _descrIndex.searchAndAccumulate(word, score: wordScore); - _readmeIndex.searchAndAccumulate(word, weight: 0.75, score: wordScore); - packageScores.multiplyAllFrom(wordScore); + _scorePool.withScore( + value: 0.0, + fn: (wordScore) { + _packageNameIndex.searchWord(word, + score: wordScore, filterOnNonZeros: packageScores); + _descrIndex.searchAndAccumulate(word, score: wordScore); + _readmeIndex.searchAndAccumulate(word, + weight: 0.75, score: wordScore); + packageScores.multiplyAllFrom(wordScore); + }, + ); } final topApiPages = @@ -483,7 +498,8 @@ class PackageNameIndex { Map search(String text) { IndexedScore? score; for (final w in splitForQuery(text)) { - final s = searchWord(w, filterOnNonZeros: score); + final s = IndexedScore(_packageNames); + searchWord(w, score: s, filterOnNonZeros: score); if (score == null) { score = s; } else { @@ -498,11 +514,12 @@ class PackageNameIndex { /// /// When [filterOnNonZeros] is present, only the indexes with an already /// non-zero value are evaluated. - IndexedScore searchWord( + void searchWord( String word, { + required IndexedScore score, IndexedScore? filterOnNonZeros, }) { - final score = IndexedScore(_packageNames); + assert(score.keys.length == _packageNames.length); final singularWord = word.length <= 3 || !word.endsWith('s') ? word : word.substring(0, word.length - 1); @@ -543,7 +560,6 @@ class PackageNameIndex { } } } - return score; } } diff --git a/app/lib/search/token_index.dart b/app/lib/search/token_index.dart index 29b7393c0d..3aded4d41c 100644 --- a/app/lib/search/token_index.dart +++ b/app/lib/search/token_index.dart @@ -100,6 +100,7 @@ class TokenIndex { /// scoring. IndexedScore searchWords(List words, {double weight = 1.0}) { IndexedScore? score; + weight = math.pow(weight, 1 / words.length).toDouble(); for (final w in words) { final s = IndexedScore(_ids); @@ -142,6 +143,30 @@ extension StringTokenIndexExt on TokenIndex { } } +/// A reusable pool for [IndexedScore] instances to spare some memory allocation. +class ScorePool { + final List _keys; + final _pool = >[]; + + ScorePool(this._keys); + + R withScore({ + required double value, + required R Function(IndexedScore score) fn, + }) { + late IndexedScore score; + if (_pool.isNotEmpty) { + score = _pool.removeLast(); + score._values.setAll(0, Iterable.generate(score.length, (_) => value)); + } else { + score = IndexedScore(_keys, value); + } + final r = fn(score); + _pool.add(score); + return r; + } +} + /// Mutable score list that can accessed via integer index. class IndexedScore { final List _keys;