Skip to content

Commit cef35eb

Browse files
committed
Auto merge of #164484 - TaKO8Ki:sparse-metadata-tables, r=<try>
Use adaptive sparse encoding for metadata tables
2 parents 2834d57 + ace7883 commit cef35eb

4 files changed

Lines changed: 71 additions & 23 deletions

File tree

‎compiler/rustc_metadata/src/rmeta/decoder.rs‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,14 @@ pub(super) trait LazyDecoder: BlobDecoder {
195195
self.read_lazy_offset_then(|pos| LazyArray::from_position_and_num_elems(pos, len))
196196
}
197197

198-
fn read_lazy_table<Ie, Id, T>(&mut self, width: usize, len: usize) -> LazyTable<Ie, Id, T> {
199-
self.read_lazy_offset_then(|pos| LazyTable::from_position_and_encoded_size(pos, width, len))
198+
fn read_lazy_table<Ie, Id, T>(
199+
&mut self,
200+
width_and_sparse: usize,
201+
len: usize,
202+
) -> LazyTable<Ie, Id, T> {
203+
self.read_lazy_offset_then(|pos| {
204+
LazyTable::from_position_and_encoded_size(pos, width_and_sparse, len)
205+
})
200206
}
201207

202208
#[inline]
@@ -688,9 +694,9 @@ impl<D: LazyDecoder, T> Decodable<D> for LazyArray<T> {
688694

689695
impl<Ie: Idx, Id: Idx, D: LazyDecoder, T> Decodable<D> for LazyTable<Ie, Id, T> {
690696
fn decode(decoder: &mut D) -> Self {
691-
let width = decoder.read_usize();
697+
let width_and_sparse = decoder.read_usize();
692698
let len = decoder.read_usize();
693-
decoder.read_lazy_table(width, len)
699+
decoder.read_lazy_table(width_and_sparse, len)
694700
}
695701
}
696702

‎compiler/rustc_metadata/src/rmeta/encoder.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<'a, 'tcx, T> Encodable<EncodeContext<'a, 'tcx>> for LazyArray<T> {
135135

136136
impl<'a, 'tcx, Ie, Id, T> Encodable<EncodeContext<'a, 'tcx>> for LazyTable<Ie, Id, T> {
137137
fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) {
138-
e.emit_usize(self.width);
138+
e.emit_usize(self.width_and_sparse);
139139
e.emit_usize(self.len);
140140
e.emit_lazy_distance(self.position);
141141
}

‎compiler/rustc_metadata/src/rmeta/mod.rs‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String {
6666
/// Metadata encoding version.
6767
/// N.B., increment this if you change the format of metadata such that
6868
/// the rustc version can't be found to compare with `rustc_version()`.
69-
const METADATA_VERSION: u8 = 11;
69+
const METADATA_VERSION: u8 = 12;
7070

7171
/// Metadata header which includes `METADATA_VERSION`.
7272
///
@@ -153,8 +153,9 @@ impl<T> LazyArray<T> {
153153
struct LazyTable<IdxEncode, IdxDecode, T> {
154154
position: NonZero<usize>,
155155
/// The encoded size of the elements of a table is selected at runtime to drop
156-
/// trailing zeroes. This is the number of bytes used for each table element.
157-
width: usize,
156+
/// trailing zeroes. The low bit selects sparse encoding; the remaining bits
157+
/// store the number of bytes used for each table element.
158+
width_and_sparse: usize,
158159
/// How many elements are in the table.
159160
len: usize,
160161
_marker: PhantomData<fn(IdxEncode, IdxDecode) -> T>,
@@ -163,10 +164,10 @@ struct LazyTable<IdxEncode, IdxDecode, T> {
163164
impl<Ie, Id, T> LazyTable<Ie, Id, T> {
164165
fn from_position_and_encoded_size(
165166
position: NonZero<usize>,
166-
width: usize,
167+
width_and_sparse: usize,
167168
len: usize,
168169
) -> LazyTable<Ie, Id, T> {
169-
LazyTable { position, width, len, _marker: PhantomData }
170+
LazyTable { position, width_and_sparse, len, _marker: PhantomData }
170171
}
171172
}
172173

‎compiler/rustc_metadata/src/rmeta/table.rs‎

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use rustc_index::Idx;
44
use crate::rmeta::decoder::MetaBlob;
55
use crate::rmeta::*;
66

7+
const SPARSE_BLOCK_LEN: usize = 64;
8+
const SPARSE_INDEX_BYTES: usize = 12;
9+
710
pub(super) trait IsDefault: Default {
811
fn is_default(&self) -> bool;
912
}
@@ -465,11 +468,6 @@ impl<Ie: Idx, Id: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>
465468
);
466469
}
467470
if !value.is_default() {
468-
// FIXME(eddyb) investigate more compact encodings for sparse tables.
469-
// On the PR @michaelwoerister mentioned:
470-
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
471-
// > trick (i.e. divide things into buckets of 32 or 64 items and then
472-
// > store bit-masks of which item in each bucket is actually serialized).
473471
let block = self.blocks.ensure_contains_elem(i, || [0; N]);
474472
value.write_to_bytes(block);
475473
if self.width != N {
@@ -483,17 +481,45 @@ impl<Ie: Idx, Id: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>
483481
let pos = buf.position();
484482

485483
let width = self.width;
484+
let len = self.blocks.len();
485+
let index_bytes = len.div_ceil(SPARSE_BLOCK_LEN) * SPARSE_INDEX_BYTES;
486+
let non_defaults = if index_bytes < len * width {
487+
self.blocks.iter().filter(|block| **block != [0; N]).count()
488+
} else {
489+
len
490+
};
491+
let sparse =
492+
u32::try_from(non_defaults).is_ok() && index_bytes + non_defaults * width < len * width;
493+
494+
if sparse {
495+
let mut base = 0u32;
496+
for chunk in self.blocks.raw.chunks(SPARSE_BLOCK_LEN) {
497+
let mut bitmap = 0u64;
498+
for (i, block) in chunk.iter().enumerate() {
499+
if *block != [0; N] {
500+
bitmap |= 1 << i;
501+
}
502+
}
503+
buf.write_array(bitmap.to_le_bytes());
504+
buf.write_array(base.to_le_bytes());
505+
base += bitmap.count_ones();
506+
}
507+
}
508+
486509
for block in &self.blocks {
487-
buf.write_with(|dest| {
488-
*dest = *block;
489-
width
490-
});
510+
if !sparse || *block != [0; N] {
511+
buf.write_with(|dest| {
512+
*dest = *block;
513+
width
514+
});
515+
}
491516
}
492517

518+
debug_assert!(width <= usize::MAX >> 1);
493519
LazyTable::from_position_and_encoded_size(
494520
NonZero::new(pos).unwrap(),
495-
width,
496-
self.blocks.len(),
521+
(width << 1) | usize::from(sparse),
522+
len,
497523
)
498524
}
499525
}
@@ -518,8 +544,23 @@ where
518544
return Default::default();
519545
}
520546

521-
let width = self.width;
522-
let start = self.position.get() + (width * i.index());
547+
let width = self.width_and_sparse >> 1;
548+
let mut position = self.position.get();
549+
let mut index = i.index();
550+
if self.width_and_sparse & 1 != 0 {
551+
let block = index / SPARSE_BLOCK_LEN;
552+
let bit = 1u64 << (index % SPARSE_BLOCK_LEN);
553+
let start = position + block * SPARSE_INDEX_BYTES;
554+
let bitmap = u64::from_le_bytes(metadata.blob()[start..start + 8].try_into().unwrap());
555+
if bitmap & bit == 0 {
556+
return Default::default();
557+
}
558+
let base =
559+
u32::from_le_bytes(metadata.blob()[start + 8..start + 12].try_into().unwrap());
560+
index = base as usize + (bitmap & (bit - 1)).count_ones() as usize;
561+
position += self.len.div_ceil(SPARSE_BLOCK_LEN) * SPARSE_INDEX_BYTES;
562+
}
563+
let start = position + width * index;
523564
let end = start + width;
524565
let bytes = &metadata.blob()[start..end];
525566

0 commit comments

Comments
 (0)