Benchmarks and test factorization
This commit is contained in:
parent
275d9e73b1
commit
e01f960ddc
|
@ -17,3 +17,10 @@ num_enum = "0.4.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_bytes = "0.11"
|
serde_bytes = "0.11"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "codec"
|
||||||
|
harness = false
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# cargo install cargo-watch
|
# cargo install cargo-watch
|
||||||
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:
|
clippy-watch:
|
||||||
cargo watch -c -x 'clippy --all-targets'
|
cargo watch -c -x 'clippy --all-targets'
|
||||||
|
|
|
@ -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);
|
|
@ -489,97 +489,3 @@ mod serde_tests {
|
||||||
assert_eq!(v, y);
|
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -426,7 +426,10 @@ impl<'de, S: BinarySource<'de>> BinaryReader<'de, S> {
|
||||||
fn peek_next_nonannotation_op(&mut self) -> ReaderResult<(Op, u8)> {
|
fn peek_next_nonannotation_op(&mut self) -> ReaderResult<(Op, u8)> {
|
||||||
loop {
|
loop {
|
||||||
match decodeop(self.peek()?)? {
|
match decodeop(self.peek()?)? {
|
||||||
(Op::Misc(0), 5) => self.skip()?,
|
(Op::Misc(0), 5) => {
|
||||||
|
self.skip()?;
|
||||||
|
self.skip_value()?;
|
||||||
|
},
|
||||||
other => return Ok(other),
|
other => return Ok(other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>),
|
||||||
|
}
|
|
@ -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(())
|
||||||
|
}
|
Loading…
Reference in New Issue