tp: Implement efficient BitVector with range intersection
Bug:277732246
Change-Id: I3ee4996243ae7f3b4a922777738494e462a83377
diff --git a/src/trace_processor/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc
index b5c2dc9..8556946 100644
--- a/src/trace_processor/containers/bit_vector.cc
+++ b/src/trace_processor/containers/bit_vector.cc
@@ -120,7 +120,7 @@
uint64_t update_unused_bits = 0;
uint8_t unused_bits_count = 0;
- // The basic premise of this loop is, for each word in |this| we find the
+ // The basic premise of this loop is, for each word in |this| we find
// enough bits from |update| to cover every set bit in the word. We then use
// the PDEP x64 instruction (or equivalent instructions/software emulation) to
// update the word and store it back in |this|.
@@ -190,5 +190,54 @@
PERFETTO_DCHECK(update.CountSetBits() == CountSetBits());
}
+BitVector BitVector::IntersectRange(uint32_t range_start,
+ uint32_t range_end) const {
+ uint32_t total_set_bits = CountSetBits();
+ if (total_set_bits == 0 || range_start >= range_end)
+ return BitVector();
+
+ uint32_t first_set_bit = IndexOfNthSet(0);
+ if (total_set_bits == 1) {
+ BitVector bv(first_set_bit, false);
+ bv.AppendTrue();
+ return bv;
+ }
+
+ // We should skip all bits until the index of first set bit bigger than
+ // |range_start|.
+ uint32_t start = std::max(range_start, first_set_bit);
+ uint32_t end = std::min(range_end, IndexOfNthSet(total_set_bits - 1));
+
+ if (start >= end)
+ return BitVector();
+
+ Builder builder(end);
+
+ // All bits before start should be empty.
+ builder.Skip(start);
+
+ uint32_t front_bits = builder.BitsUntilWordBoundaryOrFull();
+ uint32_t cur_index = start;
+ for (uint32_t i = 0; i < front_bits; ++i, ++cur_index) {
+ builder.Append(IsSet(cur_index));
+ }
+
+ PERFETTO_DCHECK(cur_index == end || cur_index % BitWord::kBits == 0);
+ uint32_t cur_words = cur_index / BitWord::kBits;
+ uint32_t full_words = builder.BitsInCompleteWordsUntilFull() / BitWord::kBits;
+ uint32_t total_full_words = cur_words + full_words;
+ for (; cur_words < total_full_words; ++cur_words) {
+ builder.AppendWord(words_[cur_words]);
+ }
+
+ uint32_t last_bits = builder.BitsUntilFull();
+ cur_index += full_words * BitWord::kBits;
+ for (uint32_t i = 0; i < last_bits; ++i, ++cur_index) {
+ builder.Append(IsSet(cur_index));
+ }
+
+ return std::move(builder).Build();
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index 2855739..bfb33eb 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -23,6 +23,7 @@
#include <algorithm>
#include <array>
+#include <optional>
#include <vector>
#include "perfetto/base/logging.h"
@@ -423,6 +424,10 @@
return bv;
}
+ // Creates a BitVector of size |end| bit the bits between |start| and |end|
+ // filled with corresponding bits |this| BitVector.
+ BitVector IntersectRange(uint32_t range_start, uint32_t range_end) const;
+
// Requests the removal of unused capacity.
// Matches the semantics of std::vector::shrink_to_fit.
void ShrinkToFit() {
diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc
index c2e4480..1715e31 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -452,6 +452,58 @@
ASSERT_FALSE(it);
}
+TEST(BitVectorUnittest, IntersectRange) {
+ BitVector bv = BitVector::Range(1, 20, [](uint32_t t) { return t % 2 == 0; });
+ BitVector intersected = bv.IntersectRange(3, 10);
+
+ ASSERT_EQ(intersected.IndexOfNthSet(0), 4u);
+ ASSERT_EQ(intersected.CountSetBits(), 3u);
+ ASSERT_EQ(intersected.size(), 10u);
+}
+
+TEST(BitVectorUnittest, IntersectRange2) {
+ BitVector bv{true, false, true, true, false, true};
+ BitVector intersected = bv.IntersectRange(2, 4);
+
+ ASSERT_EQ(intersected.IndexOfNthSet(0), 2u);
+ ASSERT_EQ(intersected.size(), 4u);
+}
+
+TEST(BitVectorUnittest, IntersectRangeAfterWord) {
+ BitVector bv =
+ BitVector::Range(64 + 1, 64 + 20, [](uint32_t t) { return t % 2 == 0; });
+ BitVector intersected = bv.IntersectRange(64 + 3, 64 + 10);
+
+ ASSERT_EQ(intersected.IndexOfNthSet(0), 64 + 4u);
+ ASSERT_EQ(intersected.CountSetBits(), 3u);
+ ASSERT_EQ(intersected.size(), 64 + 10u);
+}
+
+TEST(BitVectorUnittest, IntersectRangeSetBitsBeforeRange) {
+ BitVector bv = BitVector::Range(10, 30, [](uint32_t t) { return t < 15; });
+ BitVector intersected = bv.IntersectRange(16, 50);
+
+ ASSERT_FALSE(intersected.size());
+}
+
+TEST(BitVectorUnittest, IntersectRangeSetBitOnBoundary) {
+ BitVector bv = BitVector(10, false);
+ bv.Set(5);
+ BitVector intersected = bv.IntersectRange(5, 20);
+
+ ASSERT_EQ(intersected.size(), 6u);
+}
+
+TEST(BitVectorUnittest, IntersectRangeStressTest) {
+ BitVector bv =
+ BitVector::Range(65, 1024 + 1, [](uint32_t t) { return t % 2 == 0; });
+ BitVector intersected = bv.IntersectRange(30, 500);
+
+ ASSERT_EQ(intersected.IndexOfNthSet(0), 66u);
+ ASSERT_EQ(intersected.CountSetBits(), 217u);
+ ASSERT_EQ(intersected.size(), 500u);
+}
+
TEST(BitVectorUnittest, Range) {
BitVector bv = BitVector::Range(1, 9, [](uint32_t t) { return t % 3 == 0; });
ASSERT_EQ(bv.size(), 9u);