Benchmarks and test factorization

This commit is contained in:
Tony Garnock-Jones 2020-06-16 17:46:55 +02:00
parent 275d9e73b1
commit e01f960ddc
7 changed files with 177 additions and 96 deletions

View File

@ -17,3 +17,10 @@ num_enum = "0.4.1"
serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11"
lazy_static = "1.4.0"
[dev-dependencies]
criterion = "0.3"
[[bench]]
name = "codec"
harness = false

View File

@ -1,6 +1,9 @@
# cargo install cargo-watch
watch:
cargo watch -c -x 'check --all-targets' -x 'test --all-targets -- --nocapture'
cargo watch -c -x 'test --all-targets -- --nocapture'
bench:
cargo bench --benches
clippy-watch:
cargo watch -c -x 'clippy --all-targets'

View File

@ -0,0 +1,67 @@
use criterion::{criterion_group, criterion_main, Criterion};
use preserves::value::{self, decoder, encoder};
use preserves::{de, ser};
use std::io::Read;
#[path = "../tests/samples/mod.rs"]
mod samples;
use samples::TestCases;
pub fn bench_decoder(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let mut bs = vec![];
fh.read_to_end(&mut bs).ok();
c.bench_function("decode samples.bin", |b| b.iter(
|| decoder::from_bytes(&bs[..]).demand_next().unwrap()));
}
pub fn bench_encoder(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let v = decoder::from_read(&mut fh).demand_next().unwrap();
c.bench_function("encode samples.bin", |b| b.iter(|| {
let mut bs = vec![];
encoder::Encoder::new(&mut bs).write(&v).unwrap();
bs
}));
}
pub fn bench_de(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let mut bs = vec![];
fh.read_to_end(&mut bs).ok();
c.bench_function("deserialize samples.bin", |b| b.iter(
|| de::from_bytes::<TestCases>(&bs[..]).unwrap()));
}
pub fn bench_ser(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let v: TestCases = de::from_read(&mut fh).unwrap();
c.bench_function("serialize samples.bin", |b| b.iter(|| {
let mut bs = vec![];
ser::to_writer(&mut bs, &v).unwrap();
bs
}));
}
pub fn bench_decoder_de(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let mut bs = vec![];
fh.read_to_end(&mut bs).ok();
c.bench_function("decode-then-deserialize samples.bin", |b| b.iter(
|| value::de::from_value::<TestCases>(&decoder::from_bytes(&bs[..]).demand_next().unwrap()).unwrap()));
}
pub fn bench_ser_encoder(c: &mut Criterion) {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let v: TestCases = de::from_read(&mut fh).unwrap();
c.bench_function("serialize-then-encode samples.bin", |b| b.iter(|| {
let mut bs = vec![];
encoder::Encoder::new(&mut bs).write(&value::ser::to_value(&v)).unwrap();
bs
}));
}
criterion_group!(codec, bench_decoder, bench_encoder);
criterion_group!(serde, bench_de, bench_ser);
criterion_group!(codec_then_serde, bench_decoder_de, bench_ser_encoder);
criterion_main!(codec, serde, codec_then_serde);

View File

@ -489,97 +489,3 @@ mod serde_tests {
assert_eq!(v, y);
}
}
#[cfg(test)]
mod samples_tests {
use crate::symbol::Symbol;
use crate::error::{is_eof_io_error, is_syntax_io_error};
use crate::value::de::from_value as deserialize_from_value;
use crate::value::decoder;
use crate::value::encoder::encode_bytes;
use crate::value::{IOValue, Map};
use std::iter::Iterator;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct TestCases {
tests: Map<Symbol, TestCase>
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
enum TestCase {
Test(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
NondeterministicTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
StreamingTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
DecodeTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
ParseError(String),
ParseShort(String),
ParseEOF(String),
DecodeError(#[serde(with = "serde_bytes")] Vec<u8>),
DecodeShort(#[serde(with = "serde_bytes")] Vec<u8>),
DecodeEOF(#[serde(with = "serde_bytes")] Vec<u8>),
}
fn decode_all<'de>(bytes: &'de [u8]) -> Result<Vec<IOValue>, std::io::Error> {
let d = decoder::from_bytes(bytes);
d.collect()
}
#[test] fn run() -> std::io::Result<()> {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let mut d = decoder::from_read(&mut fh);
let tests: TestCases = deserialize_from_value(&d.next().unwrap().unwrap()).unwrap();
// println!("{:#?}", tests);
for (Symbol(ref name), ref case) in tests.tests {
println!("{:?} ==> {:?}", name, case);
match case {
TestCase::Test(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
assert_eq!(&encode_bytes(val)?, bin);
}
TestCase::NondeterministicTest(ref bin, ref val) => {
// The test cases in samples.txt are carefully
// written so that while strictly
// "nondeterministic", the order of keys in
// dictionaries follows Preserves order.
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
assert_eq!(&encode_bytes(val)?, bin);
}
TestCase::StreamingTest(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
}
TestCase::DecodeTest(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
}
TestCase::ParseError(_) => (),
TestCase::ParseShort(_) => (),
TestCase::ParseEOF(_) => (),
TestCase::DecodeError(ref bin) => {
match decode_all(&bin[..]) {
Ok(_) => panic!("Unexpected success"),
Err(e) => if is_syntax_io_error(&e) {
()
} else {
panic!("Unexpected error {:?}", e)
}
}
}
TestCase::DecodeShort(ref bin) => {
assert!(if let Err(e) = decoder::from_bytes(bin).next().unwrap() {
is_eof_io_error(&e)
} else {
false
})
}
TestCase::DecodeEOF(ref bin) => {
assert!(decoder::from_bytes(bin).next().is_none());
}
}
}
Ok(())
}
}

View File

@ -426,7 +426,10 @@ impl<'de, S: BinarySource<'de>> BinaryReader<'de, S> {
fn peek_next_nonannotation_op(&mut self) -> ReaderResult<(Op, u8)> {
loop {
match decodeop(self.peek()?)? {
(Op::Misc(0), 5) => self.skip()?,
(Op::Misc(0), 5) => {
self.skip()?;
self.skip_value()?;
},
other => return Ok(other),
}
}

View File

@ -0,0 +1,21 @@
use preserves::symbol::Symbol;
use preserves::value::{IOValue, Map};
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TestCases {
pub tests: Map<Symbol, TestCase>
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum TestCase {
Test(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
NondeterministicTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
StreamingTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
DecodeTest(#[serde(with = "serde_bytes")] Vec<u8>, IOValue),
ParseError(String),
ParseShort(String),
ParseEOF(String),
DecodeError(#[serde(with = "serde_bytes")] Vec<u8>),
DecodeShort(#[serde(with = "serde_bytes")] Vec<u8>),
DecodeEOF(#[serde(with = "serde_bytes")] Vec<u8>),
}

View File

@ -0,0 +1,74 @@
use preserves::error::{is_eof_io_error, is_syntax_io_error};
use preserves::symbol::Symbol;
use preserves::value::de::from_value as deserialize_from_value;
use preserves::value::decoder;
use preserves::value::encoder::encode_bytes;
use preserves::value::IOValue;
use std::iter::Iterator;
mod samples;
use samples::*;
fn decode_all<'de>(bytes: &'de [u8]) -> Result<Vec<IOValue>, std::io::Error> {
let d = decoder::from_bytes(bytes);
d.collect()
}
#[test] fn run() -> std::io::Result<()> {
let mut fh = std::fs::File::open("../../tests/samples.bin").unwrap();
let mut d = decoder::from_read(&mut fh);
let tests: TestCases = deserialize_from_value(&d.next().unwrap().unwrap()).unwrap();
// println!("{:#?}", tests);
for (Symbol(ref name), ref case) in tests.tests {
println!("{:?} ==> {:?}", name, case);
match case {
TestCase::Test(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
assert_eq!(&encode_bytes(val)?, bin);
}
TestCase::NondeterministicTest(ref bin, ref val) => {
// The test cases in samples.txt are carefully
// written so that while strictly
// "nondeterministic", the order of keys in
// dictionaries follows Preserves order.
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
assert_eq!(&encode_bytes(val)?, bin);
}
TestCase::StreamingTest(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
}
TestCase::DecodeTest(ref bin, ref val) => {
assert_eq!(&decode_all(&encode_bytes(val)?[..])?, &[val.clone()]);
assert_eq!(&decode_all(&bin[..])?, &[val.clone()]);
}
TestCase::ParseError(_) => (),
TestCase::ParseShort(_) => (),
TestCase::ParseEOF(_) => (),
TestCase::DecodeError(ref bin) => {
match decode_all(&bin[..]) {
Ok(_) => panic!("Unexpected success"),
Err(e) => if is_syntax_io_error(&e) {
()
} else {
panic!("Unexpected error {:?}", e)
}
}
}
TestCase::DecodeShort(ref bin) => {
assert!(if let Err(e) = decoder::from_bytes(bin).next().unwrap() {
is_eof_io_error(&e)
} else {
false
})
}
TestCase::DecodeEOF(ref bin) => {
assert!(decoder::from_bytes(bin).next().is_none());
}
}
}
Ok(())
}