blob: 2d742e759d9b301f794621fc99e8a577a5b7f481 [file] [log] [blame]
#![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<i32>,
sec: Option<i32>,
off: String,
def: String,
down: Option<i32>,
togo: Option<i32>,
ydline: Option<i32>,
description: String,
offscore: i32,
defscore: i32,
season: i32,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct NFLRowBorrowed<'a> {
gameid: &'a str,
qtr: i32,
min: Option<i32>,
sec: Option<i32>,
off: &'a str,
def: &'a str,
down: Option<i32>,
togo: Option<i32>,
ydline: Option<i32>,
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<i32>,
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<i32>,
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<usize> {
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::<Result<Vec<$type>, _>>()
.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::<Result<Vec<$type>, _>>()
.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<R, D>(rdr: &mut Reader<R>) -> 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<R, D>(rdr: &mut Reader<R>) -> u64
where
R: io::Read,
D: DeserializeOwned,
{
let mut count = 0;
for rec in rdr.deserialize::<D>() {
let _ = rec.unwrap();
count += 1;
}
count
}
fn count_iter_bytes<R: io::Read>(rdr: &mut Reader<R>) -> u64 {
let mut count = 0;
for rec in rdr.byte_records() {
count += rec.unwrap().len() as u64;
}
count
}
fn count_iter_str<R: io::Read>(rdr: &mut Reader<R>) -> u64 {
let mut count = 0;
for rec in rdr.records() {
count += rec.unwrap().len() as u64;
}
count
}
fn count_read_bytes<R: io::Read>(rdr: &mut Reader<R>) -> 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<R: io::Read>(rdr: &mut Reader<R>) -> 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<ByteRecord> {
let mut rdr = ReaderBuilder::new().has_headers(false).from_reader(data);
rdr.byte_records().collect::<Result<Vec<_>, _>>().unwrap()
}