Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Intersect to Narrow Iterate Range and Reduce Memory Allocation #9271

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 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,16 +1685,49 @@ func (l *List) Uids(opt ListOptions) (*pb.List, error) {
return out, nil
}

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

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
}

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 found {
res = append(res, uid)
if checkLimit() {
break
}
}
}
out.Uids = res
l.RUnlock()
return out, nil
}

err := l.iterate(opt.ReadTs, opt.AfterUid, func(p *pb.Posting) error {
if p.PostingType == pb.Posting_REF {
res = append(res, p.Uid)
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 {
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
Loading