#![feature(test)] extern crate test; use std::io; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use test::Bencher; use csv::{ ByteRecord, Reader, ReaderBuilder, StringRecord, Trim, Writer, WriterBuilder, }; static NFL: &'static str = include_str!("../examples/data/bench/nfl.csv"); static GAME: &'static str = include_str!("../examples/data/bench/game.csv"); static POP: &'static str = include_str!("../examples/data/bench/worldcitiespop.csv"); static MBTA: &'static str = include_str!("../examples/data/bench/gtfs-mbta-stop-times.csv"); #[derive(Debug, Serialize, Deserialize, PartialEq)] struct NFLRowOwned { gameid: String, qtr: i32, min: Option, sec: Option, off: String, def: String, down: Option, togo: Option, ydline: Option, description: String, offscore: i32, defscore: i32, season: i32, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct NFLRowBorrowed<'a> { gameid: &'a str, qtr: i32, min: Option, sec: Option, off: &'a str, def: &'a str, down: Option, togo: Option, ydline: Option, description: &'a str, offscore: i32, defscore: i32, season: i32, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct GAMERowOwned(String, String, String, String, i32, String); #[derive(Debug, Serialize, Deserialize, PartialEq)] struct GAMERowBorrowed<'a>(&'a str, &'a str, &'a str, &'a str, i32, &'a str); #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "PascalCase")] struct POPRowOwned { country: String, city: String, accent_city: String, region: String, population: Option, latitude: f64, longitude: f64, } #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "PascalCase")] struct POPRowBorrowed<'a> { country: &'a str, city: &'a str, accent_city: &'a str, region: &'a str, population: Option, latitude: f64, longitude: f64, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct MBTARowOwned { trip_id: String, arrival_time: String, departure_time: String, stop_id: String, stop_sequence: i32, stop_headsign: String, pickup_type: i32, drop_off_type: i32, timepoint: i32, } #[derive(Debug, Serialize, Deserialize, PartialEq)] struct MBTARowBorrowed<'a> { trip_id: &'a str, arrival_time: &'a str, departure_time: &'a str, stop_id: &'a str, stop_sequence: i32, stop_headsign: &'a str, pickup_type: i32, drop_off_type: i32, timepoint: i32, } #[derive(Default)] struct ByteCounter { count: usize, } impl io::Write for ByteCounter { fn write(&mut self, data: &[u8]) -> io::Result { self.count += data.len(); Ok(data.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } macro_rules! bench { ($name:ident, $data:ident, $counter:ident, $result:expr) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(data); assert_eq!($counter(&mut rdr), $result); }) } }; } macro_rules! bench_trimmed { ($name:ident, $data:ident, $counter:ident, $result:expr) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new() .has_headers(false) .trim(Trim::All) .from_reader(data); assert_eq!($counter(&mut rdr), $result); }) } }; } macro_rules! bench_serde { (no_headers, $name_de:ident, $name_ser:ident, $data:ident, $counter:ident, $type:ty, $result:expr) => { #[bench] fn $name_de(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(data); assert_eq!($counter::<_, $type>(&mut rdr), $result); }) } #[bench] fn $name_ser(b: &mut Bencher) { let data = $data.as_bytes(); let values = ReaderBuilder::new() .has_headers(false) .from_reader(data) .deserialize() .collect::, _>>() .unwrap(); let do_it = || { let mut counter = ByteCounter::default(); { let mut wtr = WriterBuilder::new() .has_headers(false) .from_writer(&mut counter); for val in &values { wtr.serialize(val).unwrap(); } } counter.count }; b.bytes = do_it() as u64; b.iter(do_it) } }; ($name_de:ident, $name_ser:ident, $data:ident, $counter:ident, $type:ty, $result:expr) => { #[bench] fn $name_de(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new().has_headers(true).from_reader(data); assert_eq!($counter::<_, $type>(&mut rdr), $result); }) } #[bench] fn $name_ser(b: &mut Bencher) { let data = $data.as_bytes(); let values = ReaderBuilder::new() .has_headers(true) .from_reader(data) .deserialize() .collect::, _>>() .unwrap(); let do_it = || { let mut counter = ByteCounter::default(); { let mut wtr = WriterBuilder::new() .has_headers(true) .from_writer(&mut counter); for val in &values { wtr.serialize(val).unwrap(); } } counter.count }; b.bytes = do_it() as u64; b.iter(do_it) } }; } macro_rules! bench_serde_borrowed_bytes { ($name:ident, $data:ident, $type:ty, $headers:expr, $result:expr) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new() .has_headers($headers) .from_reader(data); let mut count = 0; let mut rec = ByteRecord::new(); while rdr.read_byte_record(&mut rec).unwrap() { let _: $type = rec.deserialize(None).unwrap(); count += 1; } count }) } }; } macro_rules! bench_serde_borrowed_str { ($name:ident, $data:ident, $type:ty, $headers:expr, $result:expr) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; b.iter(|| { let mut rdr = ReaderBuilder::new() .has_headers($headers) .from_reader(data); let mut count = 0; let mut rec = StringRecord::new(); while rdr.read_record(&mut rec).unwrap() { let _: $type = rec.deserialize(None).unwrap(); count += 1; } count }) } }; } bench_serde!( count_nfl_deserialize_owned_bytes, count_nfl_serialize_owned_bytes, NFL, count_deserialize_owned_bytes, NFLRowOwned, 9999 ); bench_serde!( count_nfl_deserialize_owned_str, count_nfl_serialize_owned_str, NFL, count_deserialize_owned_str, NFLRowOwned, 9999 ); bench_serde_borrowed_bytes!( count_nfl_deserialize_borrowed_bytes, NFL, NFLRowBorrowed, true, 9999 ); bench_serde_borrowed_str!( count_nfl_deserialize_borrowed_str, NFL, NFLRowBorrowed, true, 9999 ); bench!(count_nfl_iter_bytes, NFL, count_iter_bytes, 130000); bench_trimmed!(count_nfl_iter_bytes_trimmed, NFL, count_iter_bytes, 130000); bench!(count_nfl_iter_str, NFL, count_iter_str, 130000); bench_trimmed!(count_nfl_iter_str_trimmed, NFL, count_iter_str, 130000); bench!(count_nfl_read_bytes, NFL, count_read_bytes, 130000); bench!(count_nfl_read_str, NFL, count_read_str, 130000); bench_serde!( no_headers, count_game_deserialize_owned_bytes, count_game_serialize_owned_bytes, GAME, count_deserialize_owned_bytes, GAMERowOwned, 100000 ); bench_serde!( no_headers, count_game_deserialize_owned_str, count_game_serialize_owned_str, GAME, count_deserialize_owned_str, GAMERowOwned, 100000 ); bench_serde_borrowed_bytes!( count_game_deserialize_borrowed_bytes, GAME, GAMERowBorrowed, true, 100000 ); bench_serde_borrowed_str!( count_game_deserialize_borrowed_str, GAME, GAMERowBorrowed, true, 100000 ); bench!(count_game_iter_bytes, GAME, count_iter_bytes, 600000); bench!(count_game_iter_str, GAME, count_iter_str, 600000); bench!(count_game_read_bytes, GAME, count_read_bytes, 600000); bench!(count_game_read_str, GAME, count_read_str, 600000); bench_serde!( count_pop_deserialize_owned_bytes, count_pop_serialize_owned_bytes, POP, count_deserialize_owned_bytes, POPRowOwned, 20000 ); bench_serde!( count_pop_deserialize_owned_str, count_pop_serialize_owned_str, POP, count_deserialize_owned_str, POPRowOwned, 20000 ); bench_serde_borrowed_bytes!( count_pop_deserialize_borrowed_bytes, POP, POPRowBorrowed, true, 20000 ); bench_serde_borrowed_str!( count_pop_deserialize_borrowed_str, POP, POPRowBorrowed, true, 20000 ); bench!(count_pop_iter_bytes, POP, count_iter_bytes, 140007); bench!(count_pop_iter_str, POP, count_iter_str, 140007); bench!(count_pop_read_bytes, POP, count_read_bytes, 140007); bench!(count_pop_read_str, POP, count_read_str, 140007); bench_serde!( count_mbta_deserialize_owned_bytes, count_mbta_serialize_owned_bytes, MBTA, count_deserialize_owned_bytes, MBTARowOwned, 9999 ); bench_serde!( count_mbta_deserialize_owned_str, count_mbta_serialize_owned_str, MBTA, count_deserialize_owned_str, MBTARowOwned, 9999 ); bench_serde_borrowed_bytes!( count_mbta_deserialize_borrowed_bytes, MBTA, MBTARowBorrowed, true, 9999 ); bench_serde_borrowed_str!( count_mbta_deserialize_borrowed_str, MBTA, MBTARowBorrowed, true, 9999 ); bench!(count_mbta_iter_bytes, MBTA, count_iter_bytes, 90000); bench!(count_mbta_iter_str, MBTA, count_iter_str, 90000); bench!(count_mbta_read_bytes, MBTA, count_read_bytes, 90000); bench!(count_mbta_read_str, MBTA, count_read_str, 90000); macro_rules! bench_write { ($name:ident, $data:ident) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; let records = collect_records(data); b.iter(|| { let mut wtr = Writer::from_writer(vec![]); for r in &records { wtr.write_record(r).unwrap(); } assert!(wtr.flush().is_ok()); }) } }; } macro_rules! bench_write_bytes { ($name:ident, $data:ident) => { #[bench] fn $name(b: &mut Bencher) { let data = $data.as_bytes(); b.bytes = data.len() as u64; let records = collect_records(data); b.iter(|| { let mut wtr = Writer::from_writer(vec![]); for r in &records { wtr.write_byte_record(r).unwrap(); } assert!(wtr.flush().is_ok()); }) } }; } bench_write!(write_nfl_record, NFL); bench_write_bytes!(write_nfl_bytes, NFL); fn count_deserialize_owned_bytes(rdr: &mut Reader) -> u64 where R: io::Read, D: DeserializeOwned, { let mut count = 0; let mut rec = ByteRecord::new(); while rdr.read_byte_record(&mut rec).unwrap() { let _: D = rec.deserialize(None).unwrap(); count += 1; } count } fn count_deserialize_owned_str(rdr: &mut Reader) -> u64 where R: io::Read, D: DeserializeOwned, { let mut count = 0; for rec in rdr.deserialize::() { let _ = rec.unwrap(); count += 1; } count } fn count_iter_bytes(rdr: &mut Reader) -> u64 { let mut count = 0; for rec in rdr.byte_records() { count += rec.unwrap().len() as u64; } count } fn count_iter_str(rdr: &mut Reader) -> u64 { let mut count = 0; for rec in rdr.records() { count += rec.unwrap().len() as u64; } count } fn count_read_bytes(rdr: &mut Reader) -> u64 { let mut count = 0; let mut rec = ByteRecord::new(); while rdr.read_byte_record(&mut rec).unwrap() { count += rec.len() as u64; } count } fn count_read_str(rdr: &mut Reader) -> u64 { let mut count = 0; let mut rec = StringRecord::new(); while rdr.read_record(&mut rec).unwrap() { count += rec.len() as u64; } count } fn collect_records(data: &[u8]) -> Vec { let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(data); rdr.byte_records().collect::, _>>().unwrap() }