Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use criterion for benchmarking #21

Merged
merged 4 commits into from Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions .travis.yml
Expand Up @@ -15,6 +15,14 @@ matrix:
allow_failures:
- rust: nightly

install:
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then rustup component add rustfmt; fi
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then rustup component add clippy; fi

script:
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then cargo fmt -- --check; fi
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then cargo clippy -- -D clippy::all; fi
- cargo test
- cargo test --no-default-features
# Validate benches still work.
- cargo bench --all -- --test
10 changes: 9 additions & 1 deletion Cargo.toml
Expand Up @@ -11,4 +11,12 @@ edition = "2018"
[features]
default = ["std"]
std = []
benchmarks = []

[[bench]]
name = "hex"
harness = false

[dev-dependencies]
criterion = "0.2"
rustc-hex = "2.0"
faster-hex = "0.3"
68 changes: 68 additions & 0 deletions benches/hex.rs
@@ -0,0 +1,68 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rustc_hex::{FromHex, ToHex};

const DATA: &[u8] = include_bytes!("../src/lib.rs");

fn bench_encode(c: &mut Criterion) {
c.bench_function("hex_encode", |b| b.iter(|| black_box(hex::encode(DATA))));
KokaKiwi marked this conversation as resolved.
Show resolved Hide resolved

c.bench_function("rustc_hex_encode", |b| {
b.iter(|| black_box(DATA.to_hex::<String>()))
});

c.bench_function("faster_hex_encode", |b| {
b.iter(|| black_box(faster_hex::hex_string(DATA).unwrap()))
});

c.bench_function("faster_hex_encode_fallback", |b| {
b.iter(|| {
let mut buffer = vec![0; DATA.len() * 2];
black_box(faster_hex::hex_encode_fallback(DATA, &mut buffer));
})
});
}

fn bench_decode(c: &mut Criterion) {
c.bench_function("hex_decode", |b| {
let hex = hex::encode(DATA);
b.iter(|| black_box(hex::decode(&hex).unwrap()))
});

c.bench_function("rustc_hex_decode", |b| {
let hex = DATA.to_hex::<String>();
b.iter(|| black_box(hex.from_hex::<Vec<u8>>().unwrap()))
});

c.bench_function("faster_hex_decode", move |b| {
let hex = faster_hex::hex_string(DATA).unwrap();
let len = DATA.len();
b.iter(|| {
let mut dst = Vec::with_capacity(len);
KokaKiwi marked this conversation as resolved.
Show resolved Hide resolved
dst.resize(len, 0);
black_box(faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap());
})
});

c.bench_function("faster_hex_decode_unchecked", |b| {
let hex = faster_hex::hex_string(DATA).unwrap();
let len = DATA.len();
b.iter(|| {
let mut dst = Vec::with_capacity(len);
dst.resize(len, 0);
black_box(faster_hex::hex_decode_unchecked(hex.as_bytes(), &mut dst));
})
});

c.bench_function("faster_hex_decode_fallback", |b| {
let hex = faster_hex::hex_string(DATA).unwrap();
let len = DATA.len();
b.iter(|| {
let mut dst = Vec::with_capacity(len);
dst.resize(len, 0);
black_box(faster_hex::hex_decode_fallback(hex.as_bytes(), &mut dst));
})
});
}

criterion_group!(benches, bench_encode, bench_decode);
criterion_main!(benches);
119 changes: 35 additions & 84 deletions src/lib.rs
@@ -1,6 +1,3 @@
#![cfg_attr(feature = "benchmarks", feature(test))]
#![cfg_attr(not(feature = "std"), no_std)]

// Copyright (c) 2013-2014 The Rust Project Developers.
// Copyright (c) 2015-2018 The rust-hex Developers.
//
Expand All @@ -26,6 +23,9 @@
//! }
//! ```

#![cfg_attr(not(feature = "std"), no_std)]
#![allow(clippy::unreadable_literal)]

#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -83,13 +83,11 @@ impl<'a> Iterator for BytesToHexChars<'a> {
fn next(&mut self) -> Option<Self::Item> {
match self.next.take() {
Some(current) => Some(current),
None => {
self.inner.next().map(|byte| {
let current = self.table[(byte >> 4) as usize] as char;
self.next = Some(self.table[(byte & 0xf) as usize] as char);
current
})
}
None => self.inner.next().map(|byte| {
let current = self.table[(byte >> 4) as usize] as char;
self.next = Some(self.table[(byte & 0xf) as usize] as char);
current
}),
}
}

Expand All @@ -109,9 +107,7 @@ impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> {
}
}

fn encode_to_iter<T: iter::FromIterator<char>>(table: &'static [u8; 16],
source: &[u8]) -> T
{
fn encode_to_iter<T: iter::FromIterator<char>>(table: &'static [u8; 16], source: &[u8]) -> T {
BytesToHexChars::new(source, table).collect()
}

Expand All @@ -130,10 +126,7 @@ impl<T: AsRef<[u8]>> ToHex for T {
pub enum FromHexError {
/// An invalid character was found. Valid ones are: `0...9`, `a...f`
/// or `A...F`.
InvalidHexCharacter {
c: char,
index: usize,
},
InvalidHexCharacter { c: char, index: usize },

/// A hex string's length needs to be even, as two digits correspond to
/// one byte.
Expand All @@ -152,20 +145,18 @@ impl std::error::Error for FromHexError {
FromHexError::InvalidHexCharacter { .. } => "invalid character",
FromHexError::OddLength => "odd number of digits",
FromHexError::InvalidStringLength => "invalid string length",

}
}
}

impl fmt::Display for FromHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromHexError::InvalidHexCharacter { c, index } =>
write!(f, "Invalid character '{}' at position {}", c, index),
FromHexError::OddLength =>
write!(f, "Odd number of digits"),
FromHexError::InvalidStringLength =>
write!(f, "Invalid string length"),
FromHexError::InvalidHexCharacter { c, index } => {
write!(f, "Invalid character '{}' at position {}", c, index)
}
FromHexError::OddLength => write!(f, "Odd number of digits"),
FromHexError::InvalidStringLength => write!(f, "Invalid string length"),
}
}
}
Expand Down Expand Up @@ -206,12 +197,10 @@ fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
b'A'..=b'F' => Ok(c - b'A' + 10),
b'a'..=b'f' => Ok(c - b'a' + 10),
b'0'..=b'9' => Ok(c - b'0'),
_ => {
Err(FromHexError::InvalidHexCharacter {
c: c as char,
index: idx,
})
}
_ => Err(FromHexError::InvalidHexCharacter {
c: c as char,
index: idx,
}),
}
}

Expand All @@ -224,9 +213,10 @@ impl FromHex for Vec<u8> {
return Err(FromHexError::OddLength);
}

hex.chunks(2).enumerate().map(|(i, pair)| {
Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?)
}).collect()
hex.chunks(2)
.enumerate()
.map(|(i, pair)| Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?))
.collect()
}
}

Expand Down Expand Up @@ -342,8 +332,7 @@ pub fn decode_to_slice<T: AsRef<[u8]>>(data: T, out: &mut [u8]) -> Result<(), Fr
}

for (i, byte) in out.iter_mut().enumerate() {
*byte = val(data[2 * i], 2 * i)? << 4
| val(data[2 * i + 1], 2 * i + 1)?;
*byte = val(data[2 * i], 2 * i)? << 4 | val(data[2 * i + 1], 2 * i + 1)?;
}

Ok(())
Expand All @@ -360,39 +349,27 @@ mod test {

#[test]
fn test_decode() {
assert_eq!(decode("666f6f626172"), Ok(String::from("foobar").into_bytes()));
assert_eq!(
decode("666f6f626172"),
Ok(String::from("foobar").into_bytes())
);
}

#[test]
pub fn test_from_hex_okay_str() {
assert_eq!(
Vec::from_hex("666f6f626172").unwrap(),
b"foobar"
);
assert_eq!(
Vec::from_hex("666F6F626172").unwrap(),
b"foobar"
);
assert_eq!(Vec::from_hex("666f6f626172").unwrap(), b"foobar");
assert_eq!(Vec::from_hex("666F6F626172").unwrap(), b"foobar");
}

#[test]
pub fn test_from_hex_okay_bytes() {
assert_eq!(
Vec::from_hex(b"666f6f626172").unwrap(),
b"foobar"
);
assert_eq!(
Vec::from_hex(b"666F6F626172").unwrap(),
b"foobar"
);
assert_eq!(Vec::from_hex(b"666f6f626172").unwrap(), b"foobar");
assert_eq!(Vec::from_hex(b"666F6F626172").unwrap(), b"foobar");
}

#[test]
pub fn test_invalid_length() {
assert_eq!(
Vec::from_hex("1").unwrap_err(),
FromHexError::OddLength
);
assert_eq!(Vec::from_hex("1").unwrap_err(), FromHexError::OddLength);
assert_eq!(
Vec::from_hex("666f6f6261721").unwrap_err(),
FromHexError::OddLength
Expand All @@ -403,10 +380,7 @@ mod test {
pub fn test_invalid_char() {
assert_eq!(
Vec::from_hex("66ag").unwrap_err(),
FromHexError::InvalidHexCharacter {
c: 'g',
index: 3
}
FromHexError::InvalidHexCharacter { c: 'g', index: 3 }
);
}

Expand All @@ -419,10 +393,7 @@ mod test {
pub fn test_from_hex_whitespace() {
assert_eq!(
Vec::from_hex("666f 6f62617").unwrap_err(),
FromHexError::InvalidHexCharacter {
c: ' ',
index: 4
}
FromHexError::InvalidHexCharacter { c: ' ', index: 4 }
);
}

Expand All @@ -439,23 +410,3 @@ mod test {
);
}
}


#[cfg(all(feature = "benchmarks", test))]
mod bench {
extern crate test;
use self::test::Bencher;

use super::*;

const MY_OWN_SOURCE: &[u8] = include_bytes!("lib.rs");

#[bench]
fn a_bench(b: &mut Bencher) {
b.bytes = MY_OWN_SOURCE.len() as u64;

b.iter(|| {
encode(MY_OWN_SOURCE)
});
}
}