Skip to content

Commit

Permalink
Use memchr::memchr{,2} for archive parsing
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
nagisa committed May 10, 2021
1 parent 2e0af3f commit 7353fff
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 13 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ crc32fast = { version = "1.2", optional = true }
flate2 = { version = "1", optional = true }
indexmap = { version = "1.1", optional = true }
wasmparser = { version = "0.57", optional = true }
memchr = { version = "2.4", optional = true, default-features = false }

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

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

#=======================================
# File format features.
archive = []
archive = ["memchr"]
coff = []
elf = []
macho = []
Expand Down
18 changes: 7 additions & 11 deletions src/read/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,12 @@ impl<'data> ArchiveMember<'data> {
parse_bsd_extended_name(&header.name[3..], data, &mut file_offset, &mut file_size)
.read_error("Invalid archive extended name length")?
} else if header.name[0] == b'/' {
let name_len =
(header.name.iter().position(|&x| x == b' ')).unwrap_or_else(|| header.name.len());
let name_len = memchr::memchr(b' ', &header.name).unwrap_or(header.name.len());
&header.name[..name_len]
} else {
let name_len = (header.name.iter().position(|&x| x == b'/'))
.or_else(|| header.name.iter().position(|&x| x == b' '))
.unwrap_or_else(|| header.name.len());
let name_len = memchr::memchr(b'/', &header.name)
.or_else(|| memchr::memchr(b' ', &header.name))
.unwrap_or(header.name.len());
&header.name[..name_len]
};

Expand Down Expand Up @@ -268,10 +267,7 @@ impl<'data> ArchiveMember<'data> {

// Ignores bytes starting from the first space.
fn parse_u64_digits(digits: &[u8], radix: u32) -> Option<u64> {
let len = digits
.iter()
.position(|&x| x == b' ')
.unwrap_or_else(|| digits.len());
let len = memchr::memchr(b' ', digits).unwrap_or(digits.len());
let digits = &digits[..len];
if digits.is_empty() {
return None;
Expand All @@ -290,7 +286,7 @@ fn parse_sysv_extended_name<'data>(digits: &[u8], names: &'data [u8]) -> Result<
let offset = parse_u64_digits(digits, 10).ok_or(())?;
let offset = offset.try_into().map_err(|_| ())?;
let name_data = names.get(offset..).ok_or(())?;
let name = match name_data.iter().position(|&x| x == b'/' || x == 0) {
let name = match memchr::memchr2(b'/', 0, name_data) {
Some(len) => &name_data[..len],
None => name_data,
};
Expand All @@ -307,7 +303,7 @@ fn parse_bsd_extended_name<'data, R: ReadRef<'data>>(
let len = parse_u64_digits(digits, 10).ok_or(())?;
*size = size.checked_sub(len).ok_or(())?;
let name_data = data.read_bytes(offset, len)?;
let name = match name_data.iter().position(|&x| x == 0) {
let name = match memchr::memchr(0, name_data) {
Some(len) => &name_data[..len],
None => name_data,
};
Expand Down

0 comments on commit 7353fff

Please sign in to comment.