Skip to content

Commit

Permalink
add streaming benchmarks (#1609)
Browse files Browse the repository at this point in the history
  • Loading branch information
Geal committed Jan 3, 2023
1 parent 93a9845 commit 934b80e
Show file tree
Hide file tree
Showing 3 changed files with 435 additions and 0 deletions.
11 changes: 11 additions & 0 deletions benchmarks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ name = "http"
path = "benches/http.rs"
harness = false

[[bench]]
name = "http_streaming"
path = "benches/http_streaming.rs"
harness = false


[[bench]]
name = "ini"
path = "benches/ini.rs"
Expand All @@ -42,3 +48,8 @@ harness = false
name = "json"
path = "benches/json.rs"
harness = false

[[bench]]
name = "json_streaming"
path = "benches/json_streaming.rs"
harness = false
199 changes: 199 additions & 0 deletions benchmarks/benches/http_streaming.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#![cfg_attr(rustfmt, rustfmt_skip)]

#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

use criterion::*;
use nom::{IResult, bytes::streaming::{tag, take_while1}, character::streaming::{line_ending, char}, multi::many};

#[cfg_attr(rustfmt, rustfmt_skip)]
#[derive(Debug)]
struct Request<'a> {
method: &'a [u8],
uri: &'a [u8],
version: &'a [u8],
}

#[derive(Debug)]
struct Header<'a> {
name: &'a [u8],
value: Vec<&'a [u8]>,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
fn is_token(c: u8) -> bool {
match c {
128..=255 => false,
0..=31 => false,
b'(' => false,
b')' => false,
b'<' => false,
b'>' => false,
b'@' => false,
b',' => false,
b';' => false,
b':' => false,
b'\\' => false,
b'"' => false,
b'/' => false,
b'[' => false,
b']' => false,
b'?' => false,
b'=' => false,
b'{' => false,
b'}' => false,
b' ' => false,
_ => true,
}
}

fn not_line_ending(c: u8) -> bool {
c != b'\r' && c != b'\n'
}

fn is_space(c: u8) -> bool {
c == b' '
}

fn is_not_space(c: u8) -> bool {
c != b' '
}
fn is_horizontal_space(c: u8) -> bool {
c == b' ' || c == b'\t'
}

fn is_version(c: u8) -> bool {
c >= b'0' && c <= b'9' || c == b'.'
}

fn request_line(input: &[u8]) -> IResult<&[u8], Request<'_>> {
let (input, method) = take_while1(is_token)(input)?;
let (input, _) = take_while1(is_space)(input)?;
let (input, uri) = take_while1(is_not_space)(input)?;
let (input, _) = take_while1(is_space)(input)?;
let (input, version) = http_version(input)?;
let (input, _) = line_ending(input)?;

Ok((input, Request {method, uri, version}))
}

fn http_version(input: &[u8]) -> IResult<&[u8], &[u8]> {
let (input, _) = tag("HTTP/")(input)?;
let (input, version) = take_while1(is_version)(input)?;

Ok((input, version))
}

fn message_header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
let (input, _) = take_while1(is_horizontal_space)(input)?;
let (input, data) = take_while1(not_line_ending)(input)?;
let (input, _) = line_ending(input)?;

Ok((input, data))
}

fn message_header(input: &[u8]) -> IResult<&[u8], Header<'_>> {
let (input, name) = take_while1(is_token)(input)?;
let (input, _) = char(':')(input)?;
let (input, value) = many(1.., message_header_value)(input)?;

Ok((input, Header{ name, value }))
}

fn request(input: &[u8]) -> IResult<&[u8], (Request<'_>, Vec<Header<'_>>)> {
let (input, req) = request_line(input)?;
let (input, h) = many(1.., message_header)(input)?;
let (input, _) = line_ending(input)?;

Ok((input, (req, h)))
}


fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
let mut buf = &data[..];
let mut v = Vec::new();
loop {
match request(buf) {
Ok((b, r)) => {
buf = b;
v.push(r);

if b.is_empty() {

//println!("{}", i);
break;
}
}
Err(e) => {
println!("error: {:?}", e);
return None;
},
}
}

Some(v)
}

/*
#[bench]
fn small_test(b: &mut Bencher) {
let data = include_bytes!("../../http-requests.txt");
b.iter(||{
parse(data)
});
}
#[bench]
fn bigger_test(b: &mut Bencher) {
let data = include_bytes!("../../bigger.txt");
b.iter(||{
parse(data)
});
}
*/

fn one_test(c: &mut Criterion) {
let data = &b"GET / HTTP/1.1
Host: www.reddit.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
"[..];

let mut http_group = c.benchmark_group("http");
http_group.throughput(Throughput::Bytes(data.len() as u64));
http_group.bench_with_input(
BenchmarkId::new("parse_streaming", data.len()),
data,
|b, data| {
b.iter(|| parse(data).unwrap());
});

http_group.finish();
}

/*
fn main() {
let mut contents: Vec<u8> = Vec::new();
{
use std::io::Read;
let mut file = File::open(env::args().nth(1).expect("File to read")).expect("Failed to open file");
let _ = file.read_to_end(&mut contents).unwrap();
}
let buf = &contents[..];
loop {
parse(buf);
}
}
*/

criterion_group!(http_streaming, one_test);
criterion_main!(http_streaming);
Loading

0 comments on commit 934b80e

Please sign in to comment.