Skip to content

Commit 7353fff

Browse files
committed
Use memchr::memchr{,2} for archive parsing
This makes parsing of the archive headers significantly faster. The `ar` example adjusted to parse the same archive 1 million times, when run with the rlib of the `object` crate itself produces the following metrics: 788.19 msec task-clock:u # 0.998 CPUs utilized 2,502,967,113 cycles:u # 3.176 GHz 7,780,571,392 instructions:u # 3.11 insn per cycle In contrast to the following for the old code: 1,061.09 msec task-clock:u # 0.998 CPUs utilized 3,374,141,510 cycles:u # 3.180 GHz 12,012,570,139 instructions:u # 3.56 insn per cycle This results in a reduction of about 1B cycles, or 25% reduction in wall clock time. Originally `perf` would show a heavy hotspot (in the area of 50% of the total runtime) in `parse_sysv_extended_name`.
1 parent 2e0af3f commit 7353fff

File tree

2 files changed

+10
-13
lines changed

2 files changed

+10
-13
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ crc32fast = { version = "1.2", optional = true }
1717
flate2 = { version = "1", optional = true }
1818
indexmap = { version = "1.1", optional = true }
1919
wasmparser = { version = "0.57", optional = true }
20+
memchr = { version = "2.4", optional = true, default-features = false }
2021

2122
# Internal feature, only used when building as part of libstd, not part of the
2223
# stable interface of this crate.
@@ -45,7 +46,7 @@ write = ["write_core", "coff", "elf", "macho"]
4546

4647
# Enable things that require libstd.
4748
# Currently, this provides an `Error` implementation.
48-
std = []
49+
std = ["memchr/std"]
4950
# Enable decompression of compressed sections.
5051
# This feature is not required if you want to do your own decompression.
5152
compression = ["flate2", "std"]
@@ -58,7 +59,7 @@ unaligned = []
5859

5960
#=======================================
6061
# File format features.
61-
archive = []
62+
archive = ["memchr"]
6263
coff = []
6364
elf = []
6465
macho = []

src/read/archive.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,12 @@ impl<'data> ArchiveMember<'data> {
197197
parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size)
198198
.read_error("Invalid archive extended name length")?
199199
} else if header.name[0] == b'/' {
200-
let name_len =
201-
(header.name.iter().position(|&x| x == b' ')).unwrap_or_else(|| header.name.len());
200+
let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len());
202201
&header.name[..name_len]
203202
} else {
204-
let name_len = (header.name.iter().position(|&x| x == b'/'))
205-
.or_else(|| header.name.iter().position(|&x| x == b' '))
206-
.unwrap_or_else(|| header.name.len());
203+
let name_len = memchr::memchr(b'/', &header.name)
204+
.or_else(|| memchr::memchr(b' ', &header.name))
205+
.unwrap_or(header.name.len());
207206
&header.name[..name_len]
208207
};
209208

@@ -268,10 +267,7 @@ impl<'data> ArchiveMember<'data> {
268267

269268
// Ignores bytes starting from the first space.
270269
fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> {
271-
let len = digits
272-
.iter()
273-
.position(|&x| x == b' ')
274-
.unwrap_or_else(|| digits.len());
270+
let len = memchr::memchr(b' ', digits).unwrap_or(digits.len());
275271
let digits = &digits[..len];
276272
if digits.is_empty() {
277273
return None;
@@ -290,7 +286,7 @@ fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<
290286
let offset = parse_u64_digits(digits, 10).ok_or(())?;
291287
let offset = offset.try_into().map_err(|_| ())?;
292288
let name_data = names.get(offset..).ok_or(())?;
293-
let name = match name_data.iter().position(|&x| x == b'/' || x == 0) {
289+
let name = match memchr::memchr2(b'/', 0, name_data) {
294290
Some(len) => &name_data[..len],
295291
None => name_data,
296292
};
@@ -307,7 +303,7 @@ fn parse_bsd_extended_name<'data, R: ReadRef<'data>>(
307303
let len = parse_u64_digits(digits, 10).ok_or(())?;
308304
*size = size.checked_sub(len).ok_or(())?;
309305
let name_data = data.read_bytes(offset, len)?;
310-
let name = match name_data.iter().position(|&x| x == 0) {
306+
let name = match memchr::memchr(0, name_data) {
311307
Some(len) => &name_data[..len],
312308
None => name_data,
313309
};

0 commit comments

Comments
 (0)