-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[ty] Garbage-collect reachability constraints #19414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9e5ba62
d588a1e
820e6ba
40c3945
4590c28
1141131
a7d6d72
39df2aa
ef06cef
31934ad
fe3ac74
1f3bddf
a823a10
441d49f
72a2d82
a15508f
597e7ad
ce9f6e2
b0cfc4f
9260f4f
7090b7e
848c573
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| //! A boxed bit slice that supports a constant-time `rank` operation. | ||
|
|
||
| use bitvec::prelude::{BitBox, Msb0}; | ||
| use get_size2::GetSize; | ||
|
|
||
| /// A boxed bit slice that supports a constant-time `rank` operation. | ||
| /// | ||
| /// This can be used to "shrink" a large vector, where you only need to keep certain elements, and | ||
| /// you want to continue to use the index in the large vector to identify each element. | ||
| /// | ||
| /// First you create a new smaller vector, keeping only the elements of the large vector that you | ||
| /// care about. Now you need a way to translate an index into the large vector (which no longer | ||
| /// exists) into the corresponding index into the smaller vector. To do that, you create a bit | ||
| /// slice, containing a bit for every element of the original large vector. Each bit in the bit | ||
| /// slice indicates whether that element of the large vector was kept in the smaller vector. And | ||
| /// the `rank` of the bit gives us the index of the element in the smaller vector. | ||
| /// | ||
| /// However, the naive implementation of `rank` is O(n) in the size of the bit slice. To address | ||
| /// that, we use a standard trick: we divide the bit slice into 64-bit chunks, and when | ||
| /// constructing the bit slice, precalculate the rank of the first bit in each chunk. Then, to | ||
| /// calculate the rank of an arbitrary bit, we first grab the precalculated rank of the chunk that | ||
| /// bit belongs to, and add the rank of the bit within its (fixed-sized) chunk. | ||
| /// | ||
| /// This trick adds O(1.5) bits of overhead per large vector element on 64-bit platforms, and O(2) | ||
| /// bits of overhead on 32-bit platforms. | ||
| #[derive(Clone, Debug, Eq, PartialEq, GetSize)] | ||
| pub(crate) struct RankBitBox { | ||
| #[get_size(size_fn = bit_box_size)] | ||
| bits: BitBox<Chunk, Msb0>, | ||
| chunk_ranks: Box<[u32]>, | ||
| } | ||
|
|
||
| fn bit_box_size(bits: &BitBox<Chunk, Msb0>) -> usize { | ||
| bits.as_raw_slice().get_heap_size() | ||
| } | ||
|
|
||
| // bitvec does not support `u64` as a Store type on 32-bit platforms | ||
| #[cfg(target_pointer_width = "64")] | ||
| type Chunk = u64; | ||
| #[cfg(not(target_pointer_width = "64"))] | ||
| type Chunk = u32; | ||
|
|
||
| const CHUNK_SIZE: usize = Chunk::BITS as usize; | ||
|
|
||
| impl RankBitBox { | ||
| pub(crate) fn from_bits(iter: impl Iterator<Item = bool>) -> Self { | ||
| let bits: BitBox<Chunk, Msb0> = iter.collect(); | ||
| let chunk_ranks = bits | ||
| .as_raw_slice() | ||
| .iter() | ||
| .scan(0u32, |rank, chunk| { | ||
| let result = *rank; | ||
| *rank += chunk.count_ones(); | ||
| Some(result) | ||
| }) | ||
| .collect(); | ||
| Self { bits, chunk_ranks } | ||
| } | ||
|
|
||
| #[inline] | ||
| pub(crate) fn get_bit(&self, index: usize) -> Option<bool> { | ||
| self.bits.get(index).map(|bit| *bit) | ||
| } | ||
|
|
||
| /// Returns the number of bits _before_ (and not including) the given index that are set. | ||
| #[inline] | ||
| pub(crate) fn rank(&self, index: usize) -> u32 { | ||
| let chunk_index = index / CHUNK_SIZE; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, too sad that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It still acts as a hint to the compiler. But yes, it's less useful compared to cross crate inlining without LTO.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Got it! Added |
||
| let index_within_chunk = index % CHUNK_SIZE; | ||
| let chunk_rank = self.chunk_ranks[chunk_index]; | ||
| if index_within_chunk == 0 { | ||
| return chunk_rank; | ||
| } | ||
|
|
||
| // To calculate the rank within the bit's chunk, we zero out the requested bit and every | ||
| // bit to the right, then count the number of 1s remaining (i.e., to the left of the | ||
| // requested bit). | ||
| let chunk = self.bits.as_raw_slice()[chunk_index]; | ||
| let chunk_mask = Chunk::MAX << (CHUNK_SIZE - index_within_chunk); | ||
| let rank_within_chunk = (chunk & chunk_mask).count_ones(); | ||
| chunk_rank + rank_within_chunk | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.