Skip to content

Commit dc26a23

Browse files
committed
rustc_metadata: reduce Lazy{,Seq} overhead by using a relative encoding.
1 parent a96abca commit dc26a23

File tree

3 files changed

+148
-26
lines changed

3 files changed

+148
-26
lines changed

src/librustc_metadata/decoder.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ pub struct DecodeContext<'a, 'tcx: 'a> {
5252
cdata: Option<&'a CrateMetadata>,
5353
from_id_range: IdRange,
5454
to_id_range: IdRange,
55+
5556
// Cache the last used filemap for translating spans as an optimization.
5657
last_filemap_index: usize,
58+
59+
lazy_state: LazyState
5760
}
5861

5962
/// Abstract over the various ways one can create metadata decoders.
@@ -73,7 +76,8 @@ pub trait Metadata<'a, 'tcx>: Copy {
7376
tcx: self.tcx(),
7477
from_id_range: id_range,
7578
to_id_range: id_range,
76-
last_filemap_index: 0
79+
last_filemap_index: 0,
80+
lazy_state: LazyState::NoNode
7781
}
7882
}
7983
}
@@ -114,13 +118,16 @@ impl<'a, 'tcx> Metadata<'a, 'tcx> for (&'a CrateMetadata, TyCtxt<'a, 'tcx, 'tcx>
114118

115119
impl<'a, 'tcx: 'a, T: Decodable> Lazy<T> {
116120
pub fn decode<M: Metadata<'a, 'tcx>>(self, meta: M) -> T {
117-
T::decode(&mut meta.decoder(self.position)).unwrap()
121+
let mut dcx = meta.decoder(self.position);
122+
dcx.lazy_state = LazyState::NodeStart(self.position);
123+
T::decode(&mut dcx).unwrap()
118124
}
119125
}
120126

121127
impl<'a, 'tcx: 'a, T: Decodable> LazySeq<T> {
122128
pub fn decode<M: Metadata<'a, 'tcx>>(self, meta: M) -> impl Iterator<Item=T> + 'a {
123129
let mut dcx = meta.decoder(self.position);
130+
dcx.lazy_state = LazyState::NodeStart(self.position);
124131
(0..self.len).map(move |_| {
125132
T::decode(&mut dcx).unwrap()
126133
})
@@ -137,12 +144,33 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
137144
}
138145

139146
fn with_position<F: FnOnce(&mut Self) -> R, R>(&mut self, pos: usize, f: F) -> R {
140-
let new = opaque::Decoder::new(self.opaque.data, pos);
141-
let old = mem::replace(&mut self.opaque, new);
147+
let new_opaque = opaque::Decoder::new(self.opaque.data, pos);
148+
let old_opaque = mem::replace(&mut self.opaque, new_opaque);
149+
let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode);
142150
let r = f(self);
143-
self.opaque = old;
151+
self.opaque = old_opaque;
152+
self.lazy_state = old_state;
144153
r
145154
}
155+
156+
fn read_lazy_distance(&mut self, min_size: usize)
157+
-> Result<usize, <Self as Decoder>::Error> {
158+
let distance = self.read_usize()?;
159+
let position = match self.lazy_state {
160+
LazyState::NoNode => {
161+
bug!("read_lazy_distance: outside of a metadata node")
162+
}
163+
LazyState::NodeStart(start) => {
164+
assert!(distance + min_size <= start);
165+
start - distance - min_size
166+
}
167+
LazyState::Previous(last_min_end) => {
168+
last_min_end + distance
169+
}
170+
};
171+
self.lazy_state = LazyState::Previous(position + min_size);
172+
Ok(position)
173+
}
146174
}
147175

148176
macro_rules! decoder_methods {
@@ -185,14 +213,19 @@ impl<'doc, 'tcx> Decoder for DecodeContext<'doc, 'tcx> {
185213

186214
impl<'a, 'tcx, T> SpecializedDecoder<Lazy<T>> for DecodeContext<'a, 'tcx> {
187215
fn specialized_decode(&mut self) -> Result<Lazy<T>, Self::Error> {
188-
Ok(Lazy::with_position(self.read_usize()?))
216+
Ok(Lazy::with_position(self.read_lazy_distance(Lazy::<T>::min_size())?))
189217
}
190218
}
191219

192220
impl<'a, 'tcx, T> SpecializedDecoder<LazySeq<T>> for DecodeContext<'a, 'tcx> {
193221
fn specialized_decode(&mut self) -> Result<LazySeq<T>, Self::Error> {
194222
let len = self.read_usize()?;
195-
Ok(LazySeq::with_position_and_length(self.read_usize()?, len))
223+
let position = if len == 0 {
224+
0
225+
} else {
226+
self.read_lazy_distance(LazySeq::<T>::min_size(len))?
227+
};
228+
Ok(LazySeq::with_position_and_length(position, len))
196229
}
197230
}
198231

src/librustc_metadata/encoder.rs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
5353
reachable: &'a NodeSet,
5454
mir_map: &'a MirMap<'tcx>,
5555

56+
lazy_state: LazyState,
5657
type_shorthands: FnvHashMap<Ty<'tcx>, usize>,
5758
predicate_shorthands: FnvHashMap<ty::Predicate<'tcx>, usize>,
5859
}
@@ -95,14 +96,17 @@ impl<'a, 'tcx> Encoder for EncodeContext<'a, 'tcx> {
9596

9697
impl<'a, 'tcx, T> SpecializedEncoder<Lazy<T>> for EncodeContext<'a, 'tcx> {
9798
fn specialized_encode(&mut self, lazy: &Lazy<T>) -> Result<(), Self::Error> {
98-
self.emit_usize(lazy.position)
99+
self.emit_lazy_distance(lazy.position, Lazy::<T>::min_size())
99100
}
100101
}
101102

102103
impl<'a, 'tcx, T> SpecializedEncoder<LazySeq<T>> for EncodeContext<'a, 'tcx> {
103104
fn specialized_encode(&mut self, seq: &LazySeq<T>) -> Result<(), Self::Error> {
104105
self.emit_usize(seq.len)?;
105-
self.emit_usize(seq.position)
106+
if seq.len == 0 {
107+
return Ok(());
108+
}
109+
self.emit_lazy_distance(seq.position, LazySeq::<T>::min_size(seq.len))
106110
}
107111
}
108112

@@ -129,24 +133,62 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
129133
self.opaque.position()
130134
}
131135

132-
pub fn lazy<T: Encodable>(&mut self, value: &T) -> Lazy<T> {
136+
fn emit_node<F: FnOnce(&mut Self, usize) -> R, R>(&mut self, f: F) -> R {
137+
assert_eq!(self.lazy_state, LazyState::NoNode);
133138
let pos = self.position();
134-
value.encode(self).unwrap();
135-
Lazy::with_position(pos)
139+
self.lazy_state = LazyState::NodeStart(pos);
140+
let r = f(self, pos);
141+
self.lazy_state = LazyState::NoNode;
142+
r
143+
}
144+
145+
fn emit_lazy_distance(&mut self, position: usize, min_size: usize)
146+
-> Result<(), <Self as Encoder>::Error> {
147+
let min_end = position + min_size;
148+
let distance = match self.lazy_state {
149+
LazyState::NoNode => {
150+
bug!("emit_lazy_distance: outside of a metadata node")
151+
}
152+
LazyState::NodeStart(start) => {
153+
assert!(min_end <= start);
154+
start - min_end
155+
}
156+
LazyState::Previous(last_min_end) => {
157+
assert!(last_min_end <= position);
158+
position - last_min_end
159+
}
160+
};
161+
self.lazy_state = LazyState::Previous(min_end);
162+
self.emit_usize(distance)
163+
}
164+
165+
pub fn lazy<T: Encodable>(&mut self, value: &T) -> Lazy<T> {
166+
self.emit_node(|ecx, pos| {
167+
value.encode(ecx).unwrap();
168+
169+
assert!(pos + Lazy::<T>::min_size() <= ecx.position());
170+
Lazy::with_position(pos)
171+
})
136172
}
137173

138174
fn lazy_seq<I, T>(&mut self, iter: I) -> LazySeq<T>
139175
where I: IntoIterator<Item=T>, T: Encodable {
140-
let pos = self.position();
141-
let len = iter.into_iter().map(|value| value.encode(self).unwrap()).count();
142-
LazySeq::with_position_and_length(pos, len)
176+
self.emit_node(|ecx, pos| {
177+
let len = iter.into_iter().map(|value| value.encode(ecx).unwrap()).count();
178+
179+
assert!(pos + LazySeq::<T>::min_size(len) <= ecx.position());
180+
LazySeq::with_position_and_length(pos, len)
181+
})
143182
}
144183

145184
fn lazy_seq_ref<'b, I, T>(&mut self, iter: I) -> LazySeq<T>
146185
where I: IntoIterator<Item=&'b T>, T: 'b + Encodable {
147-
let pos = self.position();
148-
let len = iter.into_iter().map(|value| value.encode(self).unwrap()).count();
149-
LazySeq::with_position_and_length(pos, len)
186+
self.emit_node(|ecx, pos| {
187+
let len = iter.into_iter().map(|value| value.encode(ecx).unwrap()).count();
188+
189+
assert!(pos + LazySeq::<T>::min_size(len) <= ecx.position());
190+
LazySeq::with_position_and_length(pos, len)
191+
})
150192
}
151193

152194
/// Encode the given value or a previously cached shorthand.
@@ -1262,16 +1304,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12621304
None
12631305
},
12641306

1265-
index: index,
12661307
crate_deps: crate_deps,
12671308
dylib_dependency_formats: dylib_dependency_formats,
1268-
native_libraries: native_libraries,
12691309
lang_items: lang_items,
12701310
lang_items_missing: lang_items_missing,
1311+
native_libraries: native_libraries,
1312+
codemap: codemap,
1313+
macro_defs: macro_defs,
12711314
impls: impls,
12721315
reachable_ids: reachable_ids,
1273-
macro_defs: macro_defs,
1274-
codemap: codemap
1316+
index: index,
12751317
});
12761318

12771319
let total_bytes = self.position();
@@ -1345,6 +1387,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13451387
cstore: cstore,
13461388
reachable: reachable,
13471389
mir_map: mir_map,
1390+
lazy_state: LazyState::NoNode,
13481391
type_shorthands: Default::default(),
13491392
predicate_shorthands: Default::default()
13501393
}.encode_crate_root();

src/librustc_metadata/schema.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ pub const SHORTHAND_OFFSET: usize = 0x80;
5252

5353
/// A value of type T referred to by its absolute position
5454
/// in the metadata, and which can be decoded lazily.
55+
///
56+
/// Metadata is effective a tree, encoded in post-order,
57+
/// and with the root's position written next to the header.
58+
/// That means every single `Lazy` points to some previous
59+
/// location in the metadata and is part of a larger node.
60+
///
61+
/// The first `Lazy` in a node is encoded as the backwards
62+
/// distance from the position where the containing node
63+
/// starts and where the `Lazy` points to, while the rest
64+
/// use the forward distance from the previous `Lazy`.
65+
/// Distances start at 1, as 0-byte nodes are invalid.
66+
/// Also invalid are nodes being referred in a different
67+
/// order than they were encoded in.
5568
#[must_use]
5669
pub struct Lazy<T> {
5770
pub position: usize,
@@ -65,6 +78,12 @@ impl<T> Lazy<T> {
6578
_marker: PhantomData
6679
}
6780
}
81+
82+
/// Returns the minimum encoded size of a value of type `T`.
83+
// FIXME(eddyb) Give better estimates for certain types.
84+
pub fn min_size() -> usize {
85+
1
86+
}
6887
}
6988

7089
impl<T> Copy for Lazy<T> {}
@@ -77,10 +96,16 @@ impl<T> serialize::UseSpecializedDecodable for Lazy<T> {}
7796

7897
/// A sequence of type T referred to by its absolute position
7998
/// in the metadata and length, and which can be decoded lazily.
99+
/// The sequence is a single node for the purposes of `Lazy`.
80100
///
81101
/// Unlike `Lazy<Vec<T>>`, the length is encoded next to the
82102
/// position, not at the position, which means that the length
83103
/// doesn't need to be known before encoding all the elements.
104+
///
105+
/// If the length is 0, no position is encoded, but otherwise,
106+
/// the encoding is that of `Lazy`, with the distinction that
107+
/// the minimal distance the length of the sequence, i.e.
108+
/// it's assumed there's no 0-byte element in the sequence.
84109
#[must_use]
85110
pub struct LazySeq<T> {
86111
pub len: usize,
@@ -100,6 +125,11 @@ impl<T> LazySeq<T> {
100125
_marker: PhantomData
101126
}
102127
}
128+
129+
/// Returns the minimum encoded size of `length` values of type `T`.
130+
pub fn min_size(length: usize) -> usize {
131+
length
132+
}
103133
}
104134

105135
impl<T> Copy for LazySeq<T> {}
@@ -110,6 +140,22 @@ impl<T> Clone for LazySeq<T> {
110140
impl<T> serialize::UseSpecializedEncodable for LazySeq<T> {}
111141
impl<T> serialize::UseSpecializedDecodable for LazySeq<T> {}
112142

143+
/// Encoding / decoding state for `Lazy` and `LazySeq`.
144+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
145+
pub enum LazyState {
146+
/// Outside of a metadata node.
147+
NoNode,
148+
149+
/// Inside a metadata node, and before any `Lazy` or `LazySeq`.
150+
/// The position is that of the node itself.
151+
NodeStart(usize),
152+
153+
/// Inside a metadata node, with a previous `Lazy` or `LazySeq`.
154+
/// The position is a conservative estimate of where that
155+
/// previous `Lazy` / `LazySeq` would end (see their comments).
156+
Previous(usize)
157+
}
158+
113159
#[derive(RustcEncodable, RustcDecodable)]
114160
pub struct CrateRoot {
115161
pub rustc_version: String,
@@ -121,16 +167,16 @@ pub struct CrateRoot {
121167
pub plugin_registrar_fn: Option<DefIndex>,
122168
pub macro_derive_registrar: Option<DefIndex>,
123169

124-
pub index: LazySeq<index::Index>,
125170
pub crate_deps: LazySeq<CrateDep>,
126171
pub dylib_dependency_formats: LazySeq<Option<LinkagePreference>>,
127-
pub native_libraries: LazySeq<(NativeLibraryKind, String)>,
128172
pub lang_items: LazySeq<(DefIndex, usize)>,
129173
pub lang_items_missing: LazySeq<lang_items::LangItem>,
174+
pub native_libraries: LazySeq<(NativeLibraryKind, String)>,
175+
pub codemap: LazySeq<syntax_pos::FileMap>,
176+
pub macro_defs: LazySeq<MacroDef>,
130177
pub impls: LazySeq<TraitImpls>,
131178
pub reachable_ids: LazySeq<DefIndex>,
132-
pub macro_defs: LazySeq<MacroDef>,
133-
pub codemap: LazySeq<syntax_pos::FileMap>
179+
pub index: LazySeq<index::Index>,
134180
}
135181

136182
#[derive(RustcEncodable, RustcDecodable)]

0 commit comments

Comments
 (0)