Skip to content

Commit cd3d14c

Browse files
committed
sourcebackend: move per-file aggregation to queryPositional
This saves RAM for search queries that have many matches. len(possible) now represents the number of matching files, not the total number of matches, simplifying the progress channel.
1 parent 019f48b commit cd3d14c

4 files changed

Lines changed: 94 additions & 106 deletions

File tree

‎cmd/dcs/replay.go‎

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -43,53 +43,42 @@ type measurement struct {
4343
}
4444

4545
// TODO: refactor to verifyBundle(), use bcmills concurrency pattern
46-
func verifyMatches(query string, files ranking.ResultPaths) (filesSearched int, matches int, _ error) {
46+
func verifyMatches(query string, files ranking.ResultPaths, possible []fileMatches) (filesSearched int, matches int, _ error) {
4747
rqb := []byte(query)
4848
// matchFile, err := os.Create("/tmp/matchfile.pos.txt")
4949
// if err != nil {
5050
// return m, err
5151
// }
5252
// defer matchFile.Close()
53-
work := make(chan []ranking.ResultPath)
53+
work := make(chan ranking.ResultPath)
5454
go func() {
55-
var last string
56-
var bundle []ranking.ResultPath
57-
for _, fn := range files {
58-
if fn.Path != last {
59-
if len(bundle) > 0 {
60-
work <- bundle
61-
bundle = nil
62-
}
63-
last = fn.Path
64-
filesSearched++
65-
}
66-
bundle = append(bundle, fn)
67-
}
68-
if len(bundle) > 0 {
69-
work <- bundle
55+
for _, file := range files {
56+
work <- file
7057
}
7158
close(work)
7259
}()
73-
numWorkers := min(len(files), 1000)
60+
filesSearched = len(files)
61+
numWorkers := min(len(files), 100)
7462
var matchesMu sync.Mutex
7563
var wg sync.WaitGroup
7664
wg.Add(numWorkers)
7765
for range numWorkers {
7866
go func() {
7967
defer wg.Done()
8068
buf := make([]byte, 0, 64*1024)
81-
for bundle := range work {
69+
for file := range work {
70+
bundle := possible[file.MatchIdx].matches
8271
// Turns out open+read+close is significantly faster than
8372
// mmap'ing a whole bunch of small files (most of our files are
8473
// << 64 KB).
8574
// https://eklausmeier.wordpress.com/2016/02/03/performance-comparison-mmap-versus-read-versus-fread/
86-
f, err := os.Open(bundle[0].Path)
75+
f, err := os.Open(file.Path)
8776
if err != nil {
8877
log.Fatal(err) // TODO
8978
}
9079
// Assumption: bundle is ordered from low to high (if not, we
9180
// need to traverse bundle).
92-
max := bundle[len(bundle)-1].Position + len(rqb)
81+
max := int(bundle[len(bundle)-1].Position) + len(rqb)
9382
if max > cap(buf) {
9483
buf = make([]byte, 0, max)
9584
}
@@ -104,16 +93,17 @@ func verifyMatches(query string, files ranking.ResultPaths) (filesSearched int,
10493
b := buf[:n]
10594

10695
lastPos := -1
107-
for _, fn := range bundle {
108-
if fn.Position+len(rqb) < len(b) && bytes.Equal(b[fn.Position:fn.Position+len(rqb)], rqb) {
109-
if lastPos > -1 && !bytes.ContainsRune(b[lastPos:fn.Position], '\n') {
96+
for _, m := range bundle {
97+
pos := int(m.Position)
98+
if pos+len(rqb) < len(b) && bytes.Equal(b[pos:pos+len(rqb)], rqb) {
99+
if lastPos > -1 && !bytes.ContainsRune(b[lastPos:pos], '\n') {
110100
continue // cap to one match per line, like grep()
111101
}
112102
matchesMu.Lock()
113103
matches++
114104
matchesMu.Unlock()
115-
//fmt.Fprintf(matchFile, "%s:%d\n", fn.Path, fn.Position)
116-
lastPos = fn.Position
105+
//fmt.Fprintf(matchFile, "%s:%d\n", fn.Path, pos)
106+
lastPos = pos
117107
}
118108
}
119109
}
@@ -261,16 +251,16 @@ func (si *shardedIndex) doPostingQuery(query *index.Query) []string {
261251
return possible
262252
}
263253

264-
type entry struct {
265-
fn string
266-
pos uint32
254+
type fileMatches struct {
255+
fn string
256+
matches []index.Match
267257
}
268258

269-
func (si *shardedIndex) doPostingQueryPos(query string) []entry {
259+
func (si *shardedIndex) doPostingQueryPos(query string) []fileMatches {
270260
log.Printf("doPostingQueryPos(%q)", query)
271261
var (
272262
wg sync.WaitGroup
273-
prefixed = make([][]entry, len(si.shards))
263+
prefixed = make([][]fileMatches, len(si.shards))
274264
)
275265
for i := range si.shards {
276266
wg.Add(1)
@@ -281,16 +271,24 @@ func (si *shardedIndex) doPostingQueryPos(query string) []entry {
281271
if err != nil {
282272
log.Fatalf("QueryPositional(%q): %v", query, err)
283273
}
284-
possible := make([]entry, len(matches))
285-
for idx, match := range matches {
286-
fn, err := ix.DocidMap.Lookup(match.Docid)
287-
if err != nil {
288-
log.Fatalf("DocidMap.Lookup(%v): %v", match.Docid, err)
274+
var possible []fileMatches
275+
for len(matches) > 0 {
276+
docid := matches[0].Docid
277+
n := 1
278+
// Find all matches within this document (source file).
279+
for n < len(matches) && matches[n].Docid == docid {
280+
n++
289281
}
290-
possible[idx] = entry{
291-
fn: fn,
292-
pos: match.Position,
282+
283+
fn, err := ix.DocidMap.Lookup(docid)
284+
if err != nil {
285+
log.Fatalf("DocidMap.Lookup(%v): %v", docid, err)
293286
}
287+
possible = append(possible, fileMatches{
288+
fn: fn,
289+
matches: matches[:n],
290+
})
291+
matches = matches[n:]
294292
}
295293
prefixed[i] = possible
296294
}(i)
@@ -300,7 +298,7 @@ func (si *shardedIndex) doPostingQueryPos(query string) []entry {
300298
for _, p := range prefixed {
301299
l += len(p)
302300
}
303-
possible := make([]entry, 0, l)
301+
possible := make([]fileMatches, 0, l)
304302
for i := range si.shards {
305303
possible = append(possible, prefixed[i]...)
306304
}
@@ -336,10 +334,10 @@ func (si *shardedIndex) measure(idx int, query string, pos, skipFile, skipGrep b
336334
if queryPos {
337335
possible := si.doPostingQueryPos(string(s.Rune))
338336
files := make(ranking.ResultPaths, 0, len(possible))
339-
for _, entry := range possible {
337+
for idx, entry := range possible {
340338
result := ranking.ResultPath{
341339
Path: entry.fn,
342-
Position: int(entry.pos),
340+
MatchIdx: idx,
343341
}
344342
result.Rank(si.rankingMap, &rankingopts)
345343
if result.Ranking > -1 {
@@ -349,7 +347,7 @@ func (si *shardedIndex) measure(idx int, query string, pos, skipFile, skipGrep b
349347
files = sourcebackend.FilterByKeywords(&rewritten, files)
350348
m.PostingNano = int64(time.Since(start))
351349
if !skipFile {
352-
filesSearched, matches, err := verifyMatches(string(s.Rune), files)
350+
filesSearched, matches, err := verifyMatches(string(s.Rune), files, possible)
353351
if err != nil {
354352
return m, err
355353
}

‎internal/endtoend/endtoend_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func TestEndToEnd(t *testing.T) {
111111
Data: &dcspb.Event_Progress{
112112
Progress: &dcspb.Progress{
113113
QueryId: queryId,
114-
FilesTotal: 17,
114+
FilesTotal: 8,
115115
},
116116
},
117117
},
@@ -120,8 +120,8 @@ func TestEndToEnd(t *testing.T) {
120120
Data: &dcspb.Event_Progress{
121121
Progress: &dcspb.Progress{
122122
QueryId: queryId,
123-
FilesProcessed: 17,
124-
FilesTotal: 17,
123+
FilesProcessed: 8,
124+
FilesTotal: 8,
125125
// Results: 17,
126126
},
127127
},

‎internal/ranking/pre-ranking.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func ReadRankingData(path string) (StoredRankingMap, error) {
5151
// and sorting each path.
5252
type ResultPath struct {
5353
Path string
54-
Position int
54+
MatchIdx int
5555
SourcePkgIdx [2]int
5656
Ranking float32
5757
}

0 commit comments

Comments
 (0)