Skip to content

Commit

Permalink
Use criterion for benchmarking (#21)
Browse files Browse the repository at this point in the history
* Use criterion for benchmarks
* Add faster_hex for benchmarks

Signed-off-by: koushiro <koushiro.cqx@gmail.com>
  • Loading branch information
koushiro authored and KokaKiwi committed Jul 17, 2019
1 parent 8d9e331 commit 264b89f
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 85 deletions.
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"
70 changes: 70 additions & 0 deletions benches/hex.rs
@@ -0,0 +1,70 @@
use criterion::{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(|| hex::encode(DATA)));

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

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

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

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

c.bench_function("rustc_hex_decode", |b| {
let hex = DATA.to_hex::<String>();
b.iter(|| 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);
dst.resize(len, 0);
faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap();
dst
})
});

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);
faster_hex::hex_decode_unchecked(hex.as_bytes(), &mut dst);
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);
faster_hex::hex_decode_fallback(hex.as_bytes(), &mut dst);
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)
});
}
}

0 comments on commit 264b89f

Please sign in to comment.