diff --git a/lib/storage/index_db.go b/lib/storage/index_db.go index a2703ac9f..2b260c569 100644 --- a/lib/storage/index_db.go +++ b/lib/storage/index_db.go @@ -1749,7 +1749,7 @@ func (is *indexSearch) updateMetricIDsForTagFilters(metricIDs *uint64set.Set, tf } minMetricIDs = mIDs } - metricIDs.Union(minMetricIDs) + metricIDs.UnionMayOwn(minMetricIDs) return nil } @@ -2068,7 +2068,7 @@ func (is *indexSearch) getMetricIDsForTimeRange(tr TimeRange, maxMetrics int) (* err := isLocal.getMetricIDsForDate(date, &result, maxMetrics) mu.Lock() if metricIDs.Len() < maxMetrics { - metricIDs.Union(&result) + metricIDs.UnionMayOwn(&result) } if err != nil { errGlobal = err @@ -2114,7 +2114,7 @@ func (is *indexSearch) tryUpdatingMetricIDsForDateRange(metricIDs *uint64set.Set ok, err := isLocal.tryUpdatingMetricIDsForDate(date, &result, tfs, maxMetrics) mu.Lock() if metricIDs.Len() < maxMetrics { - metricIDs.Union(&result) + metricIDs.UnionMayOwn(&result) } if !ok { okGlobal = ok @@ -2203,7 +2203,7 @@ func (is *indexSearch) tryUpdatingMetricIDsForDate(date uint64, metricIDs *uint6 return true, nil } } - metricIDs.Union(result) + metricIDs.UnionMayOwn(result) return true, nil } diff --git a/lib/uint64set/uint64set.go b/lib/uint64set/uint64set.go index 45ae9c88a..ef8f89849 100644 --- a/lib/uint64set/uint64set.go +++ b/lib/uint64set/uint64set.go @@ -165,16 +165,33 @@ func (s *Set) sort() { // Union adds all the items from a to s. func (s *Set) Union(a *Set) { + s.union(a, false) +} + +// UnionMayOwn adds all the items from a to s. +// +// It may own a if s is empty. This means that `a` cannot be used +// after the call to UnionMayOwn. +func (s *Set) UnionMayOwn(a *Set) { + s.union(a, true) +} + +func (s *Set) union(a *Set, mayOwn bool) { + if mayOwn && s.Len() < a.Len() { + // Swap `a` with `s` in order to reduce the number of iterations in ForEach loop below. + // This operation is safe only if `a` is no longer used after the call to union. + *a, *s = *s, *a + } + if a.Len() == 0 { + // Fast path - nothing to union. + return + } if s.Len() == 0 { // Fast path - just copy a. aCopy := a.Clone() *s = *aCopy return } - if a.Len() == 0 { - // Fast path - nothing to union. - return - } a.ForEach(func(part []uint64) bool { for _, x := range part { s.Add(x)