Skip to content

Commit

Permalink
Use Intersect to Narrow Iterate Range and Reduce Memory Allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
Li committed Jan 10, 2025
1 parent 3b23c92 commit 25a8814
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 21 deletions.
56 changes: 35 additions & 21 deletions posting/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -1672,10 +1672,8 @@ func (l *List) Uids(opt ListOptions) (*pb.List, error) {
if opt.First == 0 {
opt.First = math.MaxInt32
}
// Pre-assign length to make it faster.
l.RLock()
// Use approximate length for initial capacity.
res := make([]uint64, 0, l.mutationMap.len()+codec.ApproxLen(l.plist.Pack))

out := &pb.List{}
if l.mutationMap.len() == 0 && opt.Intersect != nil && len(l.plist.Splits) == 0 {
if opt.ReadTs < l.minTs {
Expand All @@ -1687,29 +1685,45 @@ func (l *List) Uids(opt ListOptions) (*pb.List, error) {
return out, nil
}

var uidMin, uidMax uint64 = 0, 0
if opt.Intersect != nil && len(opt.Intersect.Uids) > 0 {
uidMin = opt.Intersect.Uids[0]
uidMax = opt.Intersect.Uids[len(opt.Intersect.Uids)-1]
// Pre-assign length to make it faster.
res := make([]uint64, 0, x.MinInt(opt.First, len(opt.Intersect.Uids), l.mutationMap.len()+codec.ApproxLen(l.plist.Pack)))

Check failure on line 1689 in posting/list.go

View workflow job for this annotation

GitHub Actions / lint

The line is 122 characters long, which exceeds the maximum of 120 characters. (lll)

checkLimit := func() bool {
// We need the last N.
// TODO: This could be optimized by only considering some of the last UidBlocks.
if opt.First < 0 {
if len(res) > -opt.First {
res = res[1:]
}
} else if len(res) > opt.First {
return true
}
return false
}

err := l.iterate(opt.ReadTs, opt.AfterUid, func(p *pb.Posting) error {
if p.PostingType == pb.Posting_REF {
if p.Uid < uidMin {
return nil
if opt.Intersect != nil && len(opt.Intersect.Uids) < l.mutationMap.len()+codec.ApproxLen(l.plist.Pack) {
for _, uid := range opt.Intersect.Uids {
found, _, err := l.findPosting(opt.ReadTs, uid)
if err != nil {
l.RUnlock()
return out, errors.Wrapf(err, "While find posting for UIDs")
}
if p.Uid > uidMax && uidMax > 0 {
return ErrStopIteration
if found {
res = append(res, uid)
if checkLimit() {
break
}
}
res = append(res, p.Uid)
}
out.Uids = res
l.RUnlock()
return out, nil
}

if opt.First < 0 {
// We need the last N.
// TODO: This could be optimized by only considering some of the last UidBlocks.
if len(res) > -opt.First {
res = res[1:]
}
} else if len(res) > opt.First {
err := l.iterate(opt.ReadTs, opt.AfterUid, func(p *pb.Posting) error {
if p.PostingType == pb.Posting_REF {
res = append(res, p.Uid)
if checkLimit() {
return ErrStopIteration
}
}
Expand Down
32 changes: 32 additions & 0 deletions x/x.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,38 @@ func Max(a, b uint64) uint64 {
return b
}

// MinInt returns the smallest integer among the given numbers.
// The first two arguments are mandatory, additional numbers are optional.
func MinInt(a, b int, nums ...int) int {
min := a
if b < min {
min = b
}

for _, num := range nums {
if num < min {
min = num
}
}
return min
}

// MaxInt returns the largest integer among the given numbers.
// The first two arguments are mandatory, additional numbers are optional.
func MaxInt(a, b int, nums ...int) int {
max := a
if b > max {
max = b
}

for _, num := range nums {
if num > max {
max = num
}
}
return max
}

// ExponentialRetry runs the given function until it succeeds or can no longer be retried.
func ExponentialRetry(maxRetries int, waitAfterFailure time.Duration,
f func() error) error {
Expand Down

0 comments on commit 25a8814

Please sign in to comment.