Skip to content

Commit d616b53

Browse files
authored
Improve performance (#78)
Add a bunch of with_capacity and reserve calls. Avoid repeated work. All the low hanging fruit I missed in previous perf work
1 parent f41f37f commit d616b53

5 files changed

Lines changed: 79 additions & 34 deletions

File tree

src/hash_table.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ fn read_value(
6363
let mut last_hash = 0;
6464

6565
while input.len() > end_len {
66-
let mut counts = vec![];
6766
let (bytes, hash) = le_u64(input)?;
6867
last_hash = hash;
6968
if bytes.len() <= end_len {
@@ -83,6 +82,7 @@ fn read_value(
8382
)];
8483
return Err(nom::Err::Failure(VerboseError { errors }));
8584
}
85+
let mut counts = Vec::with_capacity(counts_len as usize);
8686
for _ in 0..counts_len {
8787
let (bytes, count) = le_u64(input)?;
8888
input = bytes;
@@ -136,8 +136,8 @@ fn read_value(
136136
}
137137

138138
impl HashTable {
139-
fn new() -> Self {
140-
Self(IndexMap::new())
139+
fn with_capacity(capacity: usize) -> Self {
140+
Self(IndexMap::with_capacity(capacity))
141141
}
142142

143143
/// buckets is the data the hash table buckets start at - the start of the `HashTable` in memory.
@@ -155,7 +155,7 @@ impl HashTable {
155155
debug!("Number of entries: {}", num_entries);
156156

157157
let mut payload = input;
158-
let mut result = Self::new();
158+
let mut result = Self::with_capacity(num_entries as usize);
159159
//TODO is this change right?
160160
while num_entries > 0 {
161161
let (bytes, entries) = result.parse_bucket(version, payload, num_entries)?;

src/instrumentation_profile/indexed_profile.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::summary::*;
44
use anyhow::bail;
55
use nom::{
66
error::{ContextError, ErrorKind, ParseError},
7-
number::{complete::*, Endianness},
7+
number::complete::*,
88
};
99
use rustc_hash::FxHashMap;
1010
use std::convert::TryFrom;
@@ -191,13 +191,14 @@ impl InstrProfReader for IndexedInstrProf {
191191
)?;
192192
debug!("Function hash table: {:?}", table);
193193
input = bytes;
194+
profile.reserve_records(table.0.len());
195+
profile.symtab.names.reserve(table.0.len());
194196
for ((hash, name), v) in &table.0 {
195197
let name = name.to_string();
198+
let name_hash = compute_hash(&name);
196199
profile
197200
.symtab
198-
.add_func_name(name.clone(), Some(Endianness::Little));
199-
200-
let name_hash = compute_hash(&name);
201+
.add_func_name_with_hash(name.clone(), name_hash);
201202
let record = NamedInstrProfRecord {
202203
name: Some(name),
203204
name_hash: Some(name_hash),

src/instrumentation_profile/raw_profile.rs

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,17 +248,41 @@ where
248248
} else {
249249
let mut counts = Vec::<u64>::with_capacity(data.num_counters as usize);
250250
bytes = &bytes[(counter_offset as usize)..];
251-
for _ in 0..(data.num_counters as usize) {
252-
let counter = if header.has_byte_coverage() {
251+
if header.has_byte_coverage() {
252+
if data.num_counters as usize > bytes.len() {
253+
let pos = &bytes[bytes.len()..];
254+
return Err(Err::Failure(VerboseError::from_error_kind(
255+
pos,
256+
ErrorKind::Eof,
257+
)));
258+
}
259+
for _ in 0..(data.num_counters as usize) {
253260
let counter = bytes[0];
254261
bytes = &bytes[1..];
255-
(counter == 0) as u64
256-
} else {
257-
let (b, counter) = nom_u64(header.endianness)(bytes)?;
258-
bytes = b;
259-
counter
260-
};
261-
counts.push(counter);
262+
counts.push((counter == 0) as u64);
263+
}
264+
} else {
265+
let counters_bytes = data.num_counters as usize * size_of::<u64>();
266+
if counters_bytes > bytes.len() {
267+
let pos = &bytes[bytes.len()..];
268+
return Err(Err::Failure(VerboseError::from_error_kind(
269+
pos,
270+
ErrorKind::Eof,
271+
)));
272+
}
273+
for _ in 0..(data.num_counters as usize) {
274+
let (count_bytes, remaining) = bytes.split_at(size_of::<u64>());
275+
bytes = remaining;
276+
let count_bytes = count_bytes
277+
.try_into()
278+
.expect("split_at returned exactly eight counter bytes");
279+
let counter = match header.endianness {
280+
Endianness::Little => u64::from_le_bytes(count_bytes),
281+
Endianness::Big => u64::from_be_bytes(count_bytes),
282+
Endianness::Native => u64::from_ne_bytes(count_bytes),
283+
};
284+
counts.push(counter);
285+
}
262286
}
263287
let record = InstrProfRecord {
264288
counts,
@@ -294,8 +318,8 @@ where
294318

295319
fn parse_bytes(mut input: &[u8]) -> ParseResult<'_, InstrumentationProfile> {
296320
if !input.is_empty() {
297-
let mut result = InstrumentationProfile::default();
298321
let (bytes, header) = Self::parse_header(input)?;
322+
let mut result = InstrumentationProfile::with_capacity(header.data_len as usize);
299323
// LLVM 11 and 12 are version 5. LLVM 13 is version 7
300324
let version_num = header.version();
301325
result.version = Some(version_num);
@@ -313,7 +337,7 @@ where
313337
)));
314338
}
315339
input = &bytes[(header.binary_ids_len as usize)..];
316-
let mut data_section = vec![];
340+
let mut data_section = Vec::with_capacity(header.data_len as usize);
317341
for _ in 0..header.data_len {
318342
let (bytes, data) = ProfileData::<T>::parse(input, &header)?;
319343
debug!("Parsed data section {:?}", data);
@@ -334,7 +358,7 @@ where
334358
}
335359
};
336360
input = bytes;
337-
let mut counters = vec![];
361+
let mut counters = Vec::with_capacity(data_section.len());
338362
let mut counters_delta = header.counters_delta;
339363

340364
// Okay so the counters section looks a bit hairy. So as a brief explanation.
@@ -368,7 +392,7 @@ where
368392
let (bytes, _) = take(counters_end)(input)?;
369393
input = bytes;
370394
let end_length = input.len() - header.names_len as usize;
371-
let mut symtab = Symtab::default();
395+
let mut symtab = Symtab::with_capacity(data_section.len());
372396
while input.len() > end_length {
373397
let (new_bytes, names) = parse_string_ref(input)?;
374398
debug!(

src/instrumentation_profile/types.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ pub struct Symtab {
3434
pub names: FxHashMap<u64, String>,
3535
}
3636

37+
impl Symtab {
38+
pub(crate) fn with_capacity(capacity: usize) -> Self {
39+
let mut names = FxHashMap::default();
40+
names.reserve(capacity);
41+
Self { names }
42+
}
43+
}
44+
3745
pub fn compute_hash(data: impl AsRef<[u8]>) -> u64 {
3846
let hash = md5::compute(data).0[..8].try_into().unwrap_or_default();
3947
u64::from_le_bytes(hash)
@@ -124,6 +132,22 @@ impl InstrumentationProfile {
124132
}
125133
}
126134

135+
pub(crate) fn with_capacity(capacity: usize) -> Self {
136+
let mut record_name_lookup = FxHashMap::default();
137+
record_name_lookup.reserve(capacity);
138+
Self {
139+
records: Vec::with_capacity(capacity),
140+
record_name_lookup,
141+
symtab: Symtab::with_capacity(capacity),
142+
..Default::default()
143+
}
144+
}
145+
146+
pub(crate) fn reserve_records(&mut self, additional: usize) {
147+
self.records.reserve(additional);
148+
self.record_name_lookup.reserve(additional);
149+
}
150+
127151
pub fn version(&self) -> Option<u64> {
128152
self.version
129153
}

src/lib.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,14 @@ pub fn merge_profiles<T>(files: &[T]) -> std::io::Result<InstrumentationProfile>
2525
where
2626
T: AsRef<Path>,
2727
{
28-
if files.is_empty() {
29-
Ok(InstrumentationProfile::default())
30-
} else {
31-
let mut profiles = vec![];
32-
for input in files {
33-
let profile = parse(input)?;
34-
profiles.push(profile);
35-
}
36-
let mut base = profiles.remove(0);
37-
for profile in &profiles {
38-
base.merge(profile);
39-
}
40-
Ok(base)
28+
let Some((first, rest)) = files.split_first() else {
29+
return Ok(InstrumentationProfile::default());
30+
};
31+
32+
let mut base = parse(first)?;
33+
for input in rest {
34+
let profile = parse(input)?;
35+
base.merge(&profile);
4136
}
37+
Ok(base)
4238
}

0 commit comments

Comments
 (0)