diff --git a/src/include/interval_set.h b/src/include/interval_set.h index a8446ad0808..67b7ac2e53e 100644 --- a/src/include/interval_set.h +++ b/src/include/interval_set.h @@ -286,6 +286,38 @@ class interval_set { } } } + + bool subset_size_sym(const interval_set &b) const { + auto pa = m.cbegin(), pb = b.m.cbegin(); + const auto a_end = m.cend(), b_end = b.m.cend(); + + while (pa != a_end && pb != b_end) { + while (pb->first + pb->second <= pa->first) { + ++pb; + if (pb == b_end) + return false; + } + + if (*pa == *pb) { + do { + ++pa; + ++pb; + } while (pa != a_end && pb != b_end && *pa == *pb); + continue; + } + + // interval begins before other + if (pa->first < pb->first) + return false; + // interval is longer than other + if (pa->first + pa->second > pb->first + pb->second) + return false; + + ++pa; + } + + return pa == a_end; + } public: bool operator==(const interval_set& other) const { @@ -606,6 +638,21 @@ class interval_set { } bool subset_of(const interval_set &big) const { + if (!size()) + return true; + if (size() > big.size()) + return false; + if (range_end() > big.range_end()) + return false; + + /* + * Use the lower_bound algorithm for larger size ratios + * where it performs better, but not for smaller size + * ratios where sequential search performs better. + */ + if (big.size() / size() < 10) + return subset_size_sym(big); + for (typename std::map::const_iterator i = m.begin(); i != m.end(); i++) diff --git a/src/osd/PG.cc b/src/osd/PG.cc index f1c86047c92..432d536b621 100644 --- a/src/osd/PG.cc +++ b/src/osd/PG.cc @@ -237,12 +237,10 @@ void PGPool::update(OSDMapRef map) updated = true; if (pi->maybe_updated_removed_snaps(cached_removed_snaps)) { pi->build_removed_snaps(newly_removed_snaps); - interval_set intersection; - intersection.intersection_of(newly_removed_snaps, cached_removed_snaps); - if (intersection == cached_removed_snaps) { - cached_removed_snaps.swap(newly_removed_snaps); - newly_removed_snaps = cached_removed_snaps; - newly_removed_snaps.subtract(intersection); + if (cached_removed_snaps.subset_of(newly_removed_snaps)) { + interval_set removed_snaps = newly_removed_snaps; + newly_removed_snaps.subtract(cached_removed_snaps); + cached_removed_snaps.swap(removed_snaps); } else { lgeneric_subdout(cct, osd, 0) << __func__ << " cached_removed_snaps shrank from " << cached_removed_snaps diff --git a/src/test/common/test_interval_set.cc b/src/test/common/test_interval_set.cc index 66a79a6e494..f44f35e316f 100644 --- a/src/test/common/test_interval_set.cc +++ b/src/test/common/test_interval_set.cc @@ -526,6 +526,37 @@ TYPED_TEST(IntervalSetTest, subset_of) { iset1.insert( 24, 1); ASSERT_FALSE(iset1.subset_of(iset2)); + + iset2.insert( 24, 1); + ASSERT_TRUE(iset1.subset_of(iset2)); + + iset1.insert( 30, 5); + ASSERT_FALSE(iset1.subset_of(iset2)); + + iset2.insert( 30, 5); + ASSERT_TRUE(iset1.subset_of(iset2)); + + iset2.erase( 30, 1); + ASSERT_FALSE(iset1.subset_of(iset2)); + + iset1.erase( 30, 1); + ASSERT_TRUE(iset1.subset_of(iset2)); + + iset2.erase( 34, 1); + ASSERT_FALSE(iset1.subset_of(iset2)); + + iset1.erase( 34, 1); + ASSERT_TRUE(iset1.subset_of(iset2)); + + iset1.insert( 40, 5); + ASSERT_FALSE(iset1.subset_of(iset2)); + + iset2.insert( 39, 7); + ASSERT_TRUE(iset1.subset_of(iset2)); + + iset1.insert( 50, 5); + iset2.insert( 55, 2); + ASSERT_FALSE(iset1.subset_of(iset2)); } TYPED_TEST(IntervalSetTest, span_of) {