FST  openfst-1.8.2
OpenFst Library
bitmap-index.h
Go to the documentation of this file.
1 // Copyright 2005-2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the 'License');
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an 'AS IS' BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // See www.openfst.org for extensive documentation on this weighted
16 // finite-state transducer library.
17 
18 #ifndef FST_EXTENSIONS_NGRAM_BITMAP_INDEX_H_
19 #define FST_EXTENSIONS_NGRAM_BITMAP_INDEX_H_
20 
21 #include <cstdint>
22 #include <utility>
23 #include <vector>
24 
25 #include <fst/compat.h>
26 #include <fst/log.h>
27 
28 // This class is a bitstring storage class with an index that allows
29 // seeking to the Nth set or clear bit in time O(Log(N)) (or
30 // O(log(1/density) if the relevant select index is enabled) where N is the
31 // length of the bit vector, and density is the block density of zeros/ones
32 // (for Select0/Select1 respectively). The block density is for block i is
33 // B / span_i, where B is the block size in bits (512); and span_i is
34 // Select(B * (i + 1)) - Select(B * i), the range of the bitstring that
35 // select index block i indexes. That is, B 0 (or 1) bits occur over
36 // span_i bits of the bit string.
37 //
38 // To turn this into the "standard"constant time select, there would need
39 // to be a span size threshold. Block spanning more than this would need
40 // to have the position of each bit explicitly recorded. 8k is a typical
41 // value for this threshold, but I saw no spans larger than ~6k.
42 //
43 // In addition, this class allows counting set or clear bits over ranges in
44 // constant time.
45 //
46 // This is accomplished by maintaining an index of the running popcounts
47 // of the bitstring. The index is divided into blocks that cover the
48 // size of a cache line (8 64-bit words). Each entry has one absolute count
49 // of all the 1s that appear before the block and 7 relative counts since the
50 // beginning of the block.
51 //
52 // The bitstring itself is stored as uint64s:
53 // uint64_t *bits_;
54 //
55 // The rank index looks like
56 // struct RankIndexEntry {
57 // uint32_t absolute_ones_count();
58 // uint32_t relative_ones_count_1();
59 // ...
60 // uint32_t relative_ones_count_7();
61 // };
62 // vector<RankIndexEntry> rank_index_;
63 //
64 // Where rank_index_[i].absolute_ones_count() == Rank1(512 * i), and
65 // for k in 1 .. 7:
66 // rank_index_[i].relative_ones_count_k() ==
67 // Rank1(512 * i + 64 * k) - Rank1(512 * i).
68 //
69 // This index is queried directly for Rank0 and Rank1 and binary searched
70 // for Select0 and Select1. If configured in the constructor or via
71 // BuildIndex, additional indices for Select0 and Select1 can be built
72 // to reduce these operations to O(log(1/density)) as explained above.
73 //
74 // The select indexes are stored as
75 // vector<uint32_t> select_0_index_;
76 // where
77 // select_0_index_[i] == Select0(512 * i).
78 // Similarly for select_1_index_.
79 //
80 // To save space, the absolute counts are stored as uint32_t. Therefore,
81 // only bitstrings with < 2**32 ones are supported.
82 //
83 // For each 64 bytes of input (8 8-byte words) there are 12 bytes of index
84 // (4 bytes for the absolute count and 2 * 4 bytes for the relative counts)
85 // for a 18.75% space overhead.
86 //
87 // The select indices have 6.25% overhead together.
88 
89 namespace fst {
90 
91 class BitmapIndex {
92  public:
93  static size_t StorageSize(size_t num_bits) {
94  return ((num_bits + kStorageBlockMask) >> kStorageLogBitSize);
95  }
96 
97  BitmapIndex() = default;
98  BitmapIndex(BitmapIndex&&) = default;
99  BitmapIndex& operator=(BitmapIndex&&) = default;
100 
101  // Convenience constructor to avoid a separate BuildIndex call.
102  BitmapIndex(const uint64_t* bits, std::size_t num_bits,
103  bool enable_select_0_index = false,
104  bool enable_select_1_index = false) {
105  BuildIndex(bits, num_bits, enable_select_0_index, enable_select_1_index);
106  }
107 
108  bool Get(size_t index) const { return Get(bits_, index); }
109 
110  static bool Get(const uint64_t* bits, size_t index) {
111  return (bits[index >> kStorageLogBitSize] &
112  (kOne << (index & kStorageBlockMask))) != 0;
113  }
114 
115  static void Set(uint64_t* bits, size_t index) {
116  bits[index >> kStorageLogBitSize] |= (kOne << (index & kStorageBlockMask));
117  }
118 
119  static void Clear(uint64_t* bits, size_t index) {
120  bits[index >> kStorageLogBitSize] &= ~(kOne << (index & kStorageBlockMask));
121  }
122 
123  size_t Bits() const { return num_bits_; }
124 
125  size_t ArraySize() const { return StorageSize(num_bits_); }
126 
127  // Number of bytes used to store the bit vector.
128  size_t ArrayBytes() const { return ArraySize() * sizeof(bits_[0]); }
129 
130  // Number of bytes used to store the rank index.
131  size_t IndexBytes() const {
132  return (rank_index_.size() * sizeof(rank_index_[0]) +
133  select_0_index_.size() * sizeof(select_0_index_[0]) +
134  select_1_index_.size() * sizeof(select_1_index_[0]));
135  }
136 
137  // Returns the number of one bits in the bitmap
138  size_t GetOnesCount() const {
139  // We keep an extra entry with the total count.
140  return rank_index_.back().absolute_ones_count();
141  }
142 
143  // Returns the number of one bits in positions 0 to limit - 1.
144  // REQUIRES: limit <= Bits()
145  size_t Rank1(size_t end) const;
146 
147  // Returns the number of one bits in the range start to end - 1.
148  // REQUIRES: limit <= Bits()
149  size_t GetOnesCountInRange(size_t start, size_t end) const {
150  return Rank1(end) - Rank1(start);
151  }
152 
153  // Returns the number of zero bits in positions 0 to limit - 1.
154  // REQUIRES: limit <= Bits()
155  size_t Rank0(size_t end) const { return end - Rank1(end); }
156 
157  // Returns the number of zero bits in the range start to end - 1.
158  // REQUIRES: start <= limit <= Bits()
159  size_t GetZeroesCountInRange(size_t start, size_t end) const {
160  return end - start - GetOnesCountInRange(start, end);
161  }
162 
163  // Return true if any bit between begin inclusive and end exclusive
164  // is set. 0 <= begin <= end <= Bits() is required.
165  //
166  bool TestRange(size_t start, size_t end) const {
167  // Rank1 will DCHECK the other requirements.
168  DCHECK_LE(start, end);
169  return Rank1(end) > Rank1(start);
170  }
171 
172  // Returns the offset to the nth set bit (zero based)
173  // or Bits() if index >= number of ones
174  size_t Select1(size_t bit_index) const;
175 
176  // Returns the offset to the nth clear bit (zero based)
177  // or Bits() if index > number of
178  size_t Select0(size_t bit_index) const;
179 
180  // Returns the offset of the nth and nth+1 clear bit (zero based),
181  // equivalent to two calls to Select0, but more efficient.
182  std::pair<size_t, size_t> Select0s(size_t bit_index) const;
183 
184  // Rebuilds from index for the associated Bitmap, should be called
185  // whenever changes have been made to the Bitmap or else behavior
186  // of the indexed bitmap methods will be undefined.
187  void BuildIndex(const uint64_t* bits, size_t num_bits,
188  bool enable_select_0_index = false,
189  bool enable_select_1_index = false);
190 
191  static constexpr uint64_t kOne = 1;
192  static constexpr uint32_t kStorageBitSize = 64;
193  static constexpr uint32_t kStorageLogBitSize = 6;
194 
195  private:
196  static constexpr uint32_t kUnitsPerRankIndexEntry = 8;
197  static constexpr uint32_t kBitsPerRankIndexEntry =
198  kUnitsPerRankIndexEntry * kStorageBitSize;
199  static constexpr uint32_t kStorageBlockMask = kStorageBitSize - 1;
200 
201  // TODO(jrosenstock): benchmark different values here.
202  // It's reasonable that these are the same since density is typically around
203  // 1/2.
204  static constexpr uint32_t kBitsPerSelect0Block = 512;
205  static constexpr uint32_t kBitsPerSelect1Block = 512;
206 
207  // If this many or fewer RankIndexEntry blocks need to be searched by
208  // FindRankIndexEntry use a linear search instead of a binary search.
209  // FindInvertedRankIndexEntry always uses binary search, since linear
210  // search never showed improvements on benchmarks. The value of 8 was
211  // faster than smaller values on benchmarks, but I do not feel comfortable
212  // raising it because there are very few times a higher value would
213  // make a difference. Thus, whether a higher value helps or hurts is harder
214  // to measure. TODO(jrosenstock): Try to measure with low bit density.
215  static constexpr uint32_t kMaxLinearSearchBlocks = 8;
216 
217  // A RankIndexEntry covers a block of 8 64-bit words (one cache line on
218  // x86_64 and ARM). It consists of an absolute count of all the 1s that
219  // appear before this block, and 7 relative counts for the 1s within
220  // the block. relative_ones_count_k = popcount(block[0:k]).
221  // The relative counts are stored in bitfields.
222  // A RankIndexEntry takes 12 bytes, for 12/64 = 18.75% overhead.
223  // See also documentation at the top of the file.
224  class RankIndexEntry {
225  public:
226  RankIndexEntry()
227  : absolute_ones_count_(0),
228  relative_ones_count_1_(0),
229  relative_ones_count_2_(0),
230  relative_ones_count_3_(0),
231  relative_ones_count_4_(0),
232  relative_ones_count_5_(0),
233  relative_ones_count_6_(0),
234  relative_ones_count_7_(0) {}
235 
236  uint32_t absolute_ones_count() const { return absolute_ones_count_; }
237  uint32_t relative_ones_count_1() const { return relative_ones_count_1_; }
238  uint32_t relative_ones_count_2() const { return relative_ones_count_2_; }
239  uint32_t relative_ones_count_3() const { return relative_ones_count_3_; }
240  uint32_t relative_ones_count_4() const { return relative_ones_count_4_; }
241  uint32_t relative_ones_count_5() const { return relative_ones_count_5_; }
242  uint32_t relative_ones_count_6() const { return relative_ones_count_6_; }
243  uint32_t relative_ones_count_7() const { return relative_ones_count_7_; }
244 
245  void set_absolute_ones_count(uint32_t v) { absolute_ones_count_ = v; }
246  void set_relative_ones_count_1(uint32_t v) {
247  DCHECK_LE(v, kStorageBitSize);
248  relative_ones_count_1_ = v;
249  }
250  void set_relative_ones_count_2(uint32_t v) {
251  DCHECK_LE(v, 2 * kStorageBitSize);
252  relative_ones_count_2_ = v;
253  }
254  void set_relative_ones_count_3(uint32_t v) {
255  DCHECK_LE(v, 3 * kStorageBitSize);
256  relative_ones_count_3_ = v;
257  }
258  void set_relative_ones_count_4(uint32_t v) {
259  DCHECK_LE(v, 4 * kStorageBitSize);
260  relative_ones_count_4_ = v;
261  }
262  void set_relative_ones_count_5(uint32_t v) {
263  DCHECK_LE(v, 5 * kStorageBitSize);
264  relative_ones_count_5_ = v;
265  }
266  void set_relative_ones_count_6(uint32_t v) {
267  DCHECK_LE(v, 6 * kStorageBitSize);
268  relative_ones_count_6_ = v;
269  }
270  void set_relative_ones_count_7(uint32_t v) {
271  DCHECK_LE(v, 7 * kStorageBitSize);
272  relative_ones_count_7_ = v;
273  }
274 
275  private:
276  // Popcount of 1s before this block.
277  // rank_index_[i].absolute_ones_count() == Rank1(512 * i).
278  uint32_t absolute_ones_count_;
279 
280  // Popcount of 1s since the beginning of the block.
281  // rank_index_[i].relative_ones_count_k() ==
282  // Rank1(512 * i + 64 * k) - Rank1(512 * i).
283  //
284  // Bitfield widths are set based on the maximum value these relative
285  // counts can have: relative_ones_count_1 stores values up to 64,
286  // so must be 7 bits; relative_ones_count_7 stores values up to
287  // 7 * 64 == 448, so needs 9 bits.
288  //
289  // All fields could just be 9 bits and still fit
290  // in an int64_t, but by using these values (which are also the minimum
291  // required width), no field spans 2 int32s, which may be helpful on
292  // 32-bit architectures.
293  unsigned int relative_ones_count_1_ : 7;
294  unsigned int relative_ones_count_2_ : 8;
295  unsigned int relative_ones_count_3_ : 8;
296  unsigned int relative_ones_count_4_ : 9;
297  unsigned int relative_ones_count_5_ : 9;
298  unsigned int relative_ones_count_6_ : 9;
299  unsigned int relative_ones_count_7_ : 9;
300  };
301  static_assert(sizeof(RankIndexEntry) == 4 + 8,
302  "RankIndexEntry should be 12 bytes.");
303 
304  // Returns, from the index, the count of ones up to array_index.
305  uint32_t GetIndexOnesCount(size_t array_index) const;
306 
307  // Finds the entry in the rank index for the block containing the
308  // bit_index-th 1 bit.
309  const RankIndexEntry& FindRankIndexEntry(size_t bit_index) const;
310 
311  // Finds the entry in the rank index for the block containing the
312  // bit_index-th 0 bit.
313  const RankIndexEntry& FindInvertedRankIndexEntry(size_t bit_index) const;
314 
315  // We create a combined primary and secondary index, with one extra entry
316  // to hold the total number of bits.
317  size_t rank_index_size() const {
318  return (ArraySize() + kUnitsPerRankIndexEntry - 1) /
319  kUnitsPerRankIndexEntry +
320  1;
321  }
322 
323  const uint64_t* bits_ = nullptr;
324  size_t num_bits_ = 0;
325 
326  std::vector<RankIndexEntry> rank_index_;
327 
328  // Index of positions for Select0
329  // select_0_index_[i] == Select0(kBitsPerSelect0Block * i).
330  // Empty means there is no index, otherwise, we always add an extra entry
331  // with num_bits_. Overhead is 4 bytes / 64 bytes of zeros,
332  // so 4/64 times the density of zeros. This is 6.25% * zeros_density.
333  std::vector<uint32_t> select_0_index_;
334 
335  // Index of positions for Select1
336  // select_1_index_[i] == Select1(kBitsPerSelect1Block * i).
337  // Empty means there is no index, otherwise, we always add an extra entry
338  // with num_bits_. Overhead is 4 bytes / 64 bytes of ones,
339  // so 4/64 times the density of ones. This is 6.25% * ones_density.
340  std::vector<uint32_t> select_1_index_;
341 };
342 
343 } // end namespace fst
344 
345 #endif // FST_EXTENSIONS_NGRAM_BITMAP_INDEX_H_
BitmapIndex()=default
BitmapIndex & operator=(BitmapIndex &&)=default
static void Clear(uint64_t *bits, size_t index)
Definition: bitmap-index.h:119
static constexpr uint32_t kStorageBitSize
Definition: bitmap-index.h:192
static void Set(uint64_t *bits, size_t index)
Definition: bitmap-index.h:115
void BuildIndex(const uint64_t *bits, size_t num_bits, bool enable_select_0_index=false, bool enable_select_1_index=false)
size_t Bits() const
Definition: bitmap-index.h:123
size_t Select1(size_t bit_index) const
Definition: bitmap-index.cc:49
static constexpr uint64_t kOne
Definition: bitmap-index.h:191
BitmapIndex(const uint64_t *bits, std::size_t num_bits, bool enable_select_0_index=false, bool enable_select_1_index=false)
Definition: bitmap-index.h:102
size_t Rank1(size_t end) const
Definition: bitmap-index.cc:32
size_t Select0(size_t bit_index) const
Definition: bitmap-index.cc:94
size_t GetOnesCount() const
Definition: bitmap-index.h:138
static size_t StorageSize(size_t num_bits)
Definition: bitmap-index.h:93
size_t ArrayBytes() const
Definition: bitmap-index.h:128
size_t GetZeroesCountInRange(size_t start, size_t end) const
Definition: bitmap-index.h:159
bool TestRange(size_t start, size_t end) const
Definition: bitmap-index.h:166
bool Get(size_t index) const
Definition: bitmap-index.h:108
size_t GetOnesCountInRange(size_t start, size_t end) const
Definition: bitmap-index.h:149
size_t ArraySize() const
Definition: bitmap-index.h:125
#define DCHECK_LE(x, y)
Definition: log.h:74
std::pair< size_t, size_t > Select0s(size_t bit_index) const
static bool Get(const uint64_t *bits, size_t index)
Definition: bitmap-index.h:110
size_t IndexBytes() const
Definition: bitmap-index.h:131
size_t Rank0(size_t end) const
Definition: bitmap-index.h:155
static constexpr uint32_t kStorageLogBitSize
Definition: bitmap-index.h:193