• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
18 #define SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
19 
20 #include "src/trace_processor/containers/bit_vector.h"
21 
22 namespace perfetto {
23 namespace trace_processor {
24 namespace internal {
25 
26 // Base iterator class for all iterators on BitVector.
27 //
28 // This class implements caching of one Block at a time to reduce pointer
29 // chasing. It also defers updating counts on Clear calls until the end of each
30 // block.
31 class BaseIterator {
32  public:
33   BaseIterator(BitVector* bv);
34   ~BaseIterator();
35 
36   BaseIterator(BaseIterator&&) noexcept = default;
37   BaseIterator& operator=(BaseIterator&&) = default;
38 
39   // Sets the current bit the iterator points to.
Set()40   void Set() {
41     if (!IsSet()) {
42       block_.Set(block_offset());
43 
44       is_block_changed_ = true;
45       ++set_bit_count_diff_;
46     }
47   }
48 
49   // Clears the current bit the iterator points to.
Clear()50   void Clear() {
51     if (IsSet()) {
52       block_.Clear(block_offset());
53 
54       is_block_changed_ = true;
55       --set_bit_count_diff_;
56     }
57   }
58 
59   // Returns whether the current bit the iterator points to is set.
IsSet()60   bool IsSet() { return BitVector::ConstBlock(block_).IsSet(block_offset()); }
61 
62   // Returns the index of the current bit the iterator points to.
index()63   uint32_t index() const { return index_; }
64 
65  protected:
66   // Sets the index this iterator points to the given value.
67   //
68   // This method also performs some extra work on block boundaries
69   // as it caches the block to improve performance by reducing pointer
70   // chasing on every IsSet and Clear calls.
SetIndex(uint32_t index)71   void SetIndex(uint32_t index) {
72     // We should always move the index forward.
73     PERFETTO_DCHECK(index >= index_);
74 
75     uint32_t old_index = index_;
76     index_ = index;
77 
78     // If we've reached the end of the iterator, just bail out.
79     if (index >= size_)
80       return;
81 
82     uint32_t old_block = bv_->IndexToAddress(old_index).block_idx;
83     uint32_t new_block = bv_->IndexToAddress(index).block_idx;
84 
85     // Fast path: we're in the same block so we don't need to do
86     // any other work.
87     if (PERFETTO_LIKELY(old_block == new_block))
88       return;
89 
90     // Slow path: we have to change block so this will involve flushing the old
91     // block and counts (if necessary).
92     OnBlockChange(old_block, new_block);
93   }
94 
95   // Handles flushing count changes and caches a new block.
96   void OnBlockChange(uint32_t old_block, uint32_t new_block);
97 
size()98   uint32_t size() const { return size_; }
99 
bv()100   const BitVector& bv() const { return *bv_; }
101 
102  private:
103   BaseIterator(const BaseIterator&) = delete;
104   BaseIterator& operator=(const BaseIterator&) = delete;
105 
block_offset()106   BitVector::BlockOffset block_offset() const {
107     uint16_t bit_idx_inside_block = index_ % BitVector::Block::kBits;
108 
109     BitVector::BlockOffset bo;
110     bo.word_idx = bit_idx_inside_block / BitVector::BitWord::kBits;
111     bo.bit_idx = bit_idx_inside_block % BitVector::BitWord::kBits;
112     return bo;
113   }
114 
115   uint32_t index_ = 0;
116   uint32_t size_ = 0;
117 
118   bool is_block_changed_ = false;
119   int32_t set_bit_count_diff_ = 0;
120 
121   BitVector* bv_;
122   BitVector::Block block_{bv_->words_.data()};
123 };
124 
125 // Iterator over all the bits in a bitvector.
126 class AllBitsIterator : public BaseIterator {
127  public:
128   AllBitsIterator(const BitVector*);
129 
130   // Increments the iterator to point to the next bit.
Next()131   void Next() { SetIndex(index() + 1); }
132 
133   // Increments the iterator to skip the next |n| bits and point to the
134   // following one.
135   // Precondition: n >= 1 & index() + n <= size().
Skip(uint32_t n)136   void Skip(uint32_t n) {
137     PERFETTO_DCHECK(n >= 1);
138     PERFETTO_DCHECK(index() + n <= size());
139 
140     SetIndex(index() + n);
141   }
142 
143   // Returns whether the iterator is valid.
144   operator bool() const { return index() < size(); }
145 };
146 
147 // Iterator over all the set bits in a bitvector.
148 //
149 // This iterator works by first finding a batch of indices of set bits.
150 // Then, the fast path involves simply incrementing a counter to go to
151 // the next index in this batch. On every batch boundary, we hit the
152 // slow path where we need to find another n set bits.
153 class SetBitsIterator : public BaseIterator {
154  public:
155   SetBitsIterator(const BitVector*);
156 
157   // Increments the iterator to point to the next set bit.
Next()158   void Next() {
159     // If we are out of bounds, just bail out.
160     if (PERFETTO_UNLIKELY(++set_bit_index_ >= set_bit_count_))
161       return;
162 
163     if (PERFETTO_UNLIKELY(set_bit_index_ % kBatchSize == 0))
164       ReadSetBitBatch(batch_.back() + 1);
165 
166     SetIndex(batch_[set_bit_index_ % kBatchSize]);
167   }
168 
169   // Returns whether the iterator is valid.
170   operator bool() const { return set_bit_index_ < set_bit_count_; }
171 
172   // Returns the index of the bit interms of set bits (i.e. how many times
173   // Next() has been called).
ordinal()174   uint32_t ordinal() const { return set_bit_index_; }
175 
176  private:
177   static constexpr uint32_t kBatchSize = 1024;
178 
179   // Reads a full batch of set bit indices from the bitvector and stores them
180   // in |batch_| below.
181   //
182   // This batch of indices is used on the fast path to quickly jump between
183   // set bits.
184   void ReadSetBitBatch(uint32_t start_idx);
185 
186   uint32_t set_bit_index_ = 0;
187   uint32_t set_bit_count_ = 0;
188 
189   // Contains an array of indexes; each index points to a set bit in the
190   // bitvector.
191   std::array<uint32_t, kBatchSize> batch_;
192 };
193 
194 }  // namespace internal
195 }  // namespace trace_processor
196 }  // namespace perfetto
197 
198 #endif  // SRC_TRACE_PROCESSOR_CONTAINERS_BIT_VECTOR_ITERATORS_H_
199