From 76186c096081cd4d94e810587814c74493bef25f Mon Sep 17 00:00:00 2001 From: koushiro Date: Sat, 13 Jul 2019 18:11:07 +0800 Subject: [PATCH 1/4] Use criterion for benchmarks Signed-off-by: koushiro --- .travis.yml | 8 ++++ Cargo.toml | 9 +++- benches/hex.rs | 49 ++++++++++++++++++++ src/lib.rs | 119 +++++++++++++++---------------------------------- 4 files changed, 100 insertions(+), 85 deletions(-) create mode 100644 benches/hex.rs diff --git a/.travis.yml b/.travis.yml index 2d5c565..a87f81c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 8ab26b4..56f1b8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,11 @@ edition = "2018" [features] default = ["std"] std = [] -benchmarks = [] + +[[bench]] +name = "hex" +harness = false + +[dev-dependencies] +criterion = "0.2" +rustc-hex = "2.0" diff --git a/benches/hex.rs b/benches/hex.rs new file mode 100644 index 0000000..f886e5b --- /dev/null +++ b/benches/hex.rs @@ -0,0 +1,49 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rustc_hex::{FromHex, ToHex}; + +const SMALL_DATA: &[u8] = + b"Day before yesterday I saw a rabbit, and yesterday a deer, and today, you."; +const BIG_DATA: &[u8] = include_bytes!("../src/lib.rs"); + +fn bench_small_data(c: &mut Criterion) { + c.bench_function("hex_encode_small", |b| { + b.iter(|| black_box(hex::encode(SMALL_DATA))) + }); + + c.bench_function("rustc_hex_encode_small", |b| { + b.iter(|| black_box(SMALL_DATA.to_hex::())) + }); + + c.bench_function("hex_decode_small", |b| { + let hex = hex::encode(SMALL_DATA); + b.iter(|| black_box(hex::decode(&hex).unwrap())) + }); + + c.bench_function("rustc_hex_decode_small", |b| { + let hex = hex::encode(SMALL_DATA); + b.iter(|| black_box(hex.from_hex::>().unwrap())) + }); +} + +fn bench_big_data(c: &mut Criterion) { + c.bench_function("hex_encode_big", |b| { + b.iter(|| black_box(hex::encode(BIG_DATA))) + }); + + c.bench_function("rustc_hex_encode_big", |b| { + b.iter(|| black_box(BIG_DATA.to_hex::())) + }); + + c.bench_function("hex_decode_big", |b| { + let hex = hex::encode(BIG_DATA); + b.iter(|| black_box(hex::decode(&hex).unwrap())) + }); + + c.bench_function("rustc_hex_decode_big", |b| { + let hex = hex::encode(BIG_DATA); + b.iter(|| black_box(hex.from_hex::>().unwrap())) + }); +} + +criterion_group!(benches, bench_small_data, bench_big_data); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index a31865d..9795f5b 100644 --- a/src/lib.rs +++ b/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. // @@ -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"))] @@ -83,13 +83,11 @@ impl<'a> Iterator for BytesToHexChars<'a> { fn next(&mut self) -> Option { 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 + }), } } @@ -109,9 +107,7 @@ impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> { } } -fn encode_to_iter>(table: &'static [u8; 16], - source: &[u8]) -> T -{ +fn encode_to_iter>(table: &'static [u8; 16], source: &[u8]) -> T { BytesToHexChars::new(source, table).collect() } @@ -130,10 +126,7 @@ impl> 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. @@ -152,7 +145,6 @@ impl std::error::Error for FromHexError { FromHexError::InvalidHexCharacter { .. } => "invalid character", FromHexError::OddLength => "odd number of digits", FromHexError::InvalidStringLength => "invalid string length", - } } } @@ -160,12 +152,11 @@ impl std::error::Error for FromHexError { 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"), } } } @@ -206,12 +197,10 @@ fn val(c: u8, idx: usize) -> Result { 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, + }), } } @@ -224,9 +213,10 @@ impl FromHex for Vec { 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() } } @@ -342,8 +332,7 @@ pub fn decode_to_slice>(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(()) @@ -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 @@ -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 } ); } @@ -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 } ); } @@ -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) - }); - } -} From 321b4bae333ff8e1118830d83b4439bcfabf007d Mon Sep 17 00:00:00 2001 From: koushiro Date: Sat, 13 Jul 2019 18:39:20 +0800 Subject: [PATCH 2/4] Add faster_hex for benchmarks Signed-off-by: koushiro --- Cargo.toml | 1 + benches/hex.rs | 73 +++++++++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 56f1b8c..e41963e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ harness = false [dev-dependencies] criterion = "0.2" rustc-hex = "2.0" +faster-hex = "0.3" diff --git a/benches/hex.rs b/benches/hex.rs index f886e5b..8a4e508 100644 --- a/benches/hex.rs +++ b/benches/hex.rs @@ -1,49 +1,68 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rustc_hex::{FromHex, ToHex}; -const SMALL_DATA: &[u8] = - b"Day before yesterday I saw a rabbit, and yesterday a deer, and today, you."; -const BIG_DATA: &[u8] = include_bytes!("../src/lib.rs"); +const DATA: &[u8] = include_bytes!("../src/lib.rs"); -fn bench_small_data(c: &mut Criterion) { - c.bench_function("hex_encode_small", |b| { - b.iter(|| black_box(hex::encode(SMALL_DATA))) - }); +fn bench_encode(c: &mut Criterion) { + c.bench_function("hex_encode", |b| b.iter(|| black_box(hex::encode(DATA)))); - c.bench_function("rustc_hex_encode_small", |b| { - b.iter(|| black_box(SMALL_DATA.to_hex::())) + c.bench_function("rustc_hex_encode", |b| { + b.iter(|| black_box(DATA.to_hex::())) }); - c.bench_function("hex_decode_small", |b| { - let hex = hex::encode(SMALL_DATA); - b.iter(|| black_box(hex::decode(&hex).unwrap())) + c.bench_function("faster_hex_encode", |b| { + b.iter(|| black_box(faster_hex::hex_string(DATA).unwrap())) }); - c.bench_function("rustc_hex_decode_small", |b| { - let hex = hex::encode(SMALL_DATA); - b.iter(|| black_box(hex.from_hex::>().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_big_data(c: &mut Criterion) { - c.bench_function("hex_encode_big", |b| { - b.iter(|| black_box(hex::encode(BIG_DATA))) +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_encode_big", |b| { - b.iter(|| black_box(BIG_DATA.to_hex::())) + c.bench_function("rustc_hex_decode", |b| { + let hex = DATA.to_hex::(); + b.iter(|| black_box(hex.from_hex::>().unwrap())) }); - c.bench_function("hex_decode_big", |b| { - let hex = hex::encode(BIG_DATA); - b.iter(|| black_box(hex::decode(&hex).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); + black_box(faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap()); + }) }); - c.bench_function("rustc_hex_decode_big", |b| { - let hex = hex::encode(BIG_DATA); - b.iter(|| black_box(hex.from_hex::>().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_small_data, bench_big_data); +criterion_group!(benches, bench_encode, bench_decode); criterion_main!(benches); From 693f8a58588b6f53426e704c9fd520f72d6ecdc2 Mon Sep 17 00:00:00 2001 From: koushiro Date: Sat, 13 Jul 2019 22:59:47 +0800 Subject: [PATCH 3/4] Remove black_box Signed-off-by: koushiro --- benches/hex.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/benches/hex.rs b/benches/hex.rs index 8a4e508..de3b6eb 100644 --- a/benches/hex.rs +++ b/benches/hex.rs @@ -1,23 +1,21 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; +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(|| black_box(hex::encode(DATA)))); + c.bench_function("hex_encode", |b| b.iter(|| hex::encode(DATA))); - c.bench_function("rustc_hex_encode", |b| { - b.iter(|| black_box(DATA.to_hex::())) - }); + c.bench_function("rustc_hex_encode", |b| b.iter(|| DATA.to_hex::())); c.bench_function("faster_hex_encode", |b| { - b.iter(|| black_box(faster_hex::hex_string(DATA).unwrap())) + b.iter(|| 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)); + faster_hex::hex_encode_fallback(DATA, &mut buffer); }) }); } @@ -25,12 +23,12 @@ fn bench_encode(c: &mut Criterion) { 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())) + b.iter(|| hex::decode(&hex).unwrap()) }); c.bench_function("rustc_hex_decode", |b| { let hex = DATA.to_hex::(); - b.iter(|| black_box(hex.from_hex::>().unwrap())) + b.iter(|| hex.from_hex::>().unwrap()) }); c.bench_function("faster_hex_decode", move |b| { @@ -39,7 +37,7 @@ fn bench_decode(c: &mut Criterion) { b.iter(|| { let mut dst = Vec::with_capacity(len); dst.resize(len, 0); - black_box(faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap()); + faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap(); }) }); @@ -49,7 +47,7 @@ fn bench_decode(c: &mut Criterion) { 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)); + faster_hex::hex_decode_unchecked(hex.as_bytes(), &mut dst); }) }); @@ -59,7 +57,7 @@ fn bench_decode(c: &mut Criterion) { 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)); + faster_hex::hex_decode_fallback(hex.as_bytes(), &mut dst); }) }); } From 33e60d5373244c2d366417a556e6918c0463d15a Mon Sep 17 00:00:00 2001 From: koushiro Date: Wed, 17 Jul 2019 00:41:29 +0800 Subject: [PATCH 4/4] Rename buffer to dst; Return dst in each iteration Signed-off-by: koushiro --- benches/hex.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/benches/hex.rs b/benches/hex.rs index de3b6eb..56b121f 100644 --- a/benches/hex.rs +++ b/benches/hex.rs @@ -14,8 +14,9 @@ fn bench_encode(c: &mut Criterion) { c.bench_function("faster_hex_encode_fallback", |b| { b.iter(|| { - let mut buffer = vec![0; DATA.len() * 2]; - faster_hex::hex_encode_fallback(DATA, &mut buffer); + let mut dst = vec![0; DATA.len() * 2]; + faster_hex::hex_encode_fallback(DATA, &mut dst); + dst }) }); } @@ -38,6 +39,7 @@ fn bench_decode(c: &mut Criterion) { let mut dst = Vec::with_capacity(len); dst.resize(len, 0); faster_hex::hex_decode(hex.as_bytes(), &mut dst).unwrap(); + dst }) }); @@ -48,6 +50,7 @@ fn bench_decode(c: &mut Criterion) { let mut dst = Vec::with_capacity(len); dst.resize(len, 0); faster_hex::hex_decode_unchecked(hex.as_bytes(), &mut dst); + dst }) }); @@ -58,6 +61,7 @@ fn bench_decode(c: &mut Criterion) { let mut dst = Vec::with_capacity(len); dst.resize(len, 0); faster_hex::hex_decode_fallback(hex.as_bytes(), &mut dst); + dst }) }); }