diff --git a/implementations/rust/oo/benches/codec.rs b/implementations/rust/oo/benches/codec.rs index 700e33e..e09c5b5 100644 --- a/implementations/rust/oo/benches/codec.rs +++ b/implementations/rust/oo/benches/codec.rs @@ -1,18 +1,25 @@ use criterion::{criterion_group, criterion_main, Criterion}; use oo::BinarySource; use oo::BytesBinarySource; +use oo::Domain; use oo::IOBinarySource; use oo::IOValueDomainCodec; use oo::IOValues; use oo::PackedWriter; +use oo::PlainValue; use oo::Reader; use oo::ValueImpl; use oo::packed::annotated_iovalue_from_bytes; +use oo::packed::view::ViewIterator; use std::fs::File; use std::io::Read; use std::io::Seek; use std::io; +fn deep_standalone_copy>(v: V) -> PlainValue<'static, D> { + oo::copy_via(&v, &mut |d| Ok::<_, ()>(oo::owned(oo::Embedded::new(d.clone())))).unwrap() +} + pub fn bench_decoder_bytes(c: &mut Criterion) { let mut fh = File::open("../../../tests/samples.bin").unwrap(); let mut bs = vec![]; @@ -87,12 +94,109 @@ pub fn large_testdata_encoder(c: &mut Criterion) { }); } +pub fn large_testdata_decoder_view(c: &mut Criterion) { + c.bench_function("(oo) view-decode testdata.bin", |b| { + let mut fh = File::open("benches/testdata.bin").unwrap(); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + b.iter(|| { + let mut r = ViewIterator::new(&bs); + while let Some(_) = r.next() {} + }) + }); +} + +pub fn large_testdata_decoder_view_clone(c: &mut Criterion) { + c.bench_function("(oo) view-decode testdata.bin with cloning", |b| { + let mut fh = File::open("benches/testdata.bin").unwrap(); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + b.iter(|| { + let mut r = ViewIterator::new(&bs); + while let Some(v) = r.next() { + v.value_clone(); + } + }) + }); +} + +pub fn large_testdata_decoder_view_deepcopy(c: &mut Criterion) { + c.bench_function("(oo) view-decode testdata.bin with deep copy", |b| { + let mut fh = File::open("benches/testdata.bin").unwrap(); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + b.iter(|| { + let mut r = ViewIterator::new(&bs); + while let Some(v) = r.next() { + deep_standalone_copy(&*v); + } + }) + }); +} + +pub fn large_testdata_encoder_view(c: &mut Criterion) { + c.bench_function("(oo) view-encode testdata.bin", |b| { + let mut fh = io::BufReader::new(File::open("benches/testdata.bin").unwrap()); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + let vs = ViewIterator::new(&bs).collect::>(); + b.iter_with_large_drop(|| { + let mut bs = vec![]; + let mut w = PackedWriter::new(&mut bs); + let mut enc = IOValueDomainCodec; + for v in &vs { + v.write(&mut w, &mut enc).unwrap(); + } + bs + }) + }); +} + +pub fn large_testdata_encoder_view_clone(c: &mut Criterion) { + c.bench_function("(oo) view-encode testdata.bin with cloning", |b| { + let mut fh = io::BufReader::new(File::open("benches/testdata.bin").unwrap()); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + let vs = ViewIterator::new(&bs).map(|v| v.value_clone()).collect::>(); + b.iter_with_large_drop(|| { + let mut bs = vec![]; + let mut w = PackedWriter::new(&mut bs); + let mut enc = IOValueDomainCodec; + for v in &vs { + v.write(&mut w, &mut enc).unwrap(); + } + bs + }) + }); +} + +pub fn large_testdata_encoder_view_deepcopy(c: &mut Criterion) { + c.bench_function("(oo) view-encode testdata.bin with deep copy", |b| { + let mut fh = io::BufReader::new(File::open("benches/testdata.bin").unwrap()); + let mut bs = vec![]; + fh.read_to_end(&mut bs).ok(); + let vs = ViewIterator::new(&bs).map(|v| deep_standalone_copy(&*v)).collect::>(); + b.iter_with_large_drop(|| { + let mut bs = vec![]; + let mut w = PackedWriter::new(&mut bs); + let mut enc = IOValueDomainCodec; + for v in &vs { + v.write(&mut w, &mut enc).unwrap(); + } + bs + }) + }); +} + criterion_group!(codec, bench_decoder_bytes, bench_decoder_file, bench_decoder_buffered_file, bench_encoder); criterion_group!{ name = large_testdata; config = Criterion::default().sample_size(10); - targets = large_testdata_decoder_with_ann, large_testdata_decoder_without_ann, large_testdata_encoder + targets = + large_testdata_decoder_with_ann, large_testdata_decoder_without_ann, large_testdata_encoder, + large_testdata_decoder_view, large_testdata_decoder_view_clone, large_testdata_decoder_view_deepcopy, + large_testdata_encoder_view, large_testdata_encoder_view_clone, large_testdata_encoder_view_deepcopy } criterion_main!(codec, large_testdata); diff --git a/implementations/rust/oo/src/packed/view.rs b/implementations/rust/oo/src/packed/view.rs index 2f4e236..1765ae0 100644 --- a/implementations/rust/oo/src/packed/view.rs +++ b/implementations/rust/oo/src/packed/view.rs @@ -82,10 +82,16 @@ fn skip_value(packed: &[u8], mut i: usize) -> io::Result { } Tag::SmallInteger(_) => i + 1, Tag::MediumInteger(n) => i + 1 + (n as usize), - Tag::SignedInteger | Tag::String | Tag:: ByteString | Tag:: Symbol => { + Tag::SignedInteger | Tag::ByteString => { let (n, i) = varint(packed, i + 1)?; i + (n as usize) } + Tag::String | Tag::Symbol => { + let (n, i) = varint(packed, i + 1)?; + std::str::from_utf8(&packed[i .. i + (n as usize)]).map_err( + |_| io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8"))?; + i + (n as usize) + } Tag::Record | Tag::Sequence | Tag::Set | Tag::Dictionary => { i = i + 1; while tag_at(packed, i)? != Tag::End { @@ -224,7 +230,10 @@ impl<'de, Packed: AsRef<[u8]> + 'de> ValueImpl for View<'de, Packed> { fn as_string(&self) -> Option> { match self.tag() { - Tag::String => Some(Cow::Borrowed(std::str::from_utf8(self.atom_chunk()).unwrap())), + Tag::String => Some(Cow::Borrowed(unsafe { + // SAFETY: we already checked in the View constructor + std::str::from_utf8_unchecked(self.atom_chunk()) + })), _ => None, } } @@ -238,7 +247,10 @@ impl<'de, Packed: AsRef<[u8]> + 'de> ValueImpl for View<'de, Packed> { fn as_symbol(&self) -> Option> { match self.tag() { - Tag::Symbol => Some(Cow::Borrowed(std::str::from_utf8(self.atom_chunk()).unwrap())), + Tag::Symbol => Some(Cow::Borrowed(unsafe { + // SAFETY: we already checked in the View constructor + std::str::from_utf8_unchecked(self.atom_chunk()) + })), _ => None, } } @@ -265,7 +277,7 @@ impl<'de, Packed: AsRef<[u8]> + 'de> ValueImpl for View<'de, Packed> { } fn iter(&self) -> Box> + '_> { - let mut i = Box::new(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 ..])); + let mut i = Box::new(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 .. self.value_end])); match self.tag() { Tag::Record => { i.next(); () } Tag::Sequence => (), @@ -296,7 +308,7 @@ impl<'de, Packed: AsRef<[u8]> + 'de> ValueImpl for View<'de, Packed> { fn entries(&self) -> Box, Value<'_, IOValue>)> + '_> { if !self.is_dictionary() { panic!("Not a dictionary") } - Box::new(DictionaryAdapter(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 ..]))) + Box::new(DictionaryAdapter(ViewIterator::new(&self.packed.as_ref()[self.value_offset + 1 .. self.value_end]))) } fn is_embedded(&self) -> bool {