diff --git a/benches/parse.rs b/benches/parse.rs index a953dd7..775eb89 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -1,7 +1,6 @@ - use std::time::Duration; -use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; +use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput, BatchSize}; const REQ_SHORT: &[u8] = b"\ GET / HTTP/1.0\r\n\ @@ -21,25 +20,37 @@ Connection: keep-alive\r\n\ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n"; fn req(c: &mut Criterion) { - let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; - let mut req = httparse::Request::new(&mut headers); - c.benchmark_group("req") .throughput(Throughput::Bytes(REQ.len() as u64)) - .bench_function("req", |b| b.iter(|| { - assert_eq!(black_box(req.parse(REQ).unwrap()), httparse::Status::Complete(REQ.len())); - })); + .bench_function("req", |b| b.iter_batched_ref(|| { + [httparse::Header { + name: "", + value: &[], + }; 16] + },|headers| { + let mut req = httparse::Request::new(headers); + assert_eq!( + black_box(req.parse(REQ).unwrap()), + httparse::Status::Complete(REQ.len()) + ); + }, BatchSize::SmallInput)); } fn req_short(c: &mut Criterion) { - let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; - let mut req = httparse::Request::new(&mut headers); - c.benchmark_group("req_short") .throughput(Throughput::Bytes(REQ_SHORT.len() as u64)) - .bench_function("req_short", |b| b.iter(|| { - assert_eq!(black_box(req.parse(REQ_SHORT).unwrap()), httparse::Status::Complete(REQ_SHORT.len())); - })); + .bench_function("req_short", |b| b.iter_batched_ref(|| { + [httparse::Header { + name: "", + value: &[], + }; 16] + },|headers| { + let mut req = httparse::Request::new(headers); + assert_eq!( + req.parse(black_box(REQ_SHORT)).unwrap(), + httparse::Status::Complete(REQ_SHORT.len()) + ); + }, BatchSize::SmallInput)); } const RESP_SHORT: &[u8] = b"\ @@ -62,25 +73,38 @@ Connection: keep-alive\r\n\ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.xxxxxxxxxx.x; __utmz=xxxxxxxxx.xxxxxxxxxx.x.x.utmccn=(referral)|utmcsr=reader.livedoor.com|utmcct=/reader/|utmcmd=referral|padding=under256\r\n\r\n"; fn resp(c: &mut Criterion) { - let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; - let mut resp = httparse::Response::new(&mut headers); - c.benchmark_group("resp") .throughput(Throughput::Bytes(RESP.len() as u64)) - .bench_function("resp", |b| b.iter(|| { - assert_eq!(black_box(resp.parse(RESP).unwrap()), httparse::Status::Complete(RESP.len())); - })); + .bench_function("resp", |b| b.iter_batched_ref(|| { + [httparse::Header { + name: "", + value: &[], + }; 16] + }, |headers| { + let mut resp = httparse::Response::new(headers); + assert_eq!( + resp.parse(black_box(RESP)).unwrap(), + httparse::Status::Complete(RESP.len()) + ); + }, BatchSize::SmallInput)); } fn resp_short(c: &mut Criterion) { - let mut headers = [httparse::Header{ name: "", value: &[] }; 16]; - let mut resp = httparse::Response::new(&mut headers); - c.benchmark_group("resp_short") .throughput(Throughput::Bytes(RESP_SHORT.len() as u64)) - .bench_function("resp_short", |b| b.iter(|| { - assert_eq!(black_box(resp.parse(RESP_SHORT).unwrap()), httparse::Status::Complete(RESP_SHORT.len())); - })); + .bench_function("resp_short", |b| b.iter_batched_ref(|| { + [httparse::Header { + name: "", + value: &[], + }; 16] + }, + |headers| { + let mut resp = httparse::Response::new(headers); + assert_eq!( + resp.parse(black_box(RESP_SHORT)).unwrap(), + httparse::Status::Complete(RESP_SHORT.len()) + ); + }, BatchSize::SmallInput)); } fn uri(c: &mut Criterion) { @@ -88,17 +112,15 @@ fn uri(c: &mut Criterion) { c.benchmark_group("uri") .throughput(Throughput::Bytes(input.len() as u64)) .bench_function(name, |b| b.iter(|| { - black_box({ - let mut b = httparse::_benchable::Bytes::new(input); - httparse::_benchable::parse_uri(&mut b).unwrap() - }); + let mut b = httparse::_benchable::Bytes::new(black_box(input)); + httparse::_benchable::parse_uri(&mut b).unwrap() })); } const S: &[u8] = b" "; const CHUNK64: &[u8] = b"/wp-content/uploads/2022/08/31/hello-kitty-darth-vader-pink.webp"; let chunk_4k = CHUNK64.repeat(64); - + // 1b to 4096b for p in 0..=12 { let n = 1 << p; @@ -108,37 +130,34 @@ fn uri(c: &mut Criterion) { fn header(c: &mut Criterion) { fn _header(c: &mut Criterion, name: &str, input: &'static [u8]) { - let mut headers = [httparse::EMPTY_HEADER; 128]; c.benchmark_group("header") .throughput(Throughput::Bytes(input.len() as u64)) - .bench_function(name, |b| b.iter(|| { - { - let _ = httparse::parse_headers(input, &mut headers).unwrap(); - }; - black_box(()); - })); + .bench_function(name, |b| b.iter_batched_ref(|| [httparse::EMPTY_HEADER; 128],|headers| { + let status = httparse::parse_headers(black_box(input), headers).unwrap(); + black_box(status.unwrap()).0 + }, BatchSize::SmallInput)); } - + const RN: &[u8] = b"\r\n"; const RNRN: &[u8] = b"\r\n\r\n"; const TINY_RN: &[u8] = b"a: b\r\n"; // minimal header line const XFOOBAR: &[u8] = b"X-Foobar"; let xfoobar_4k = XFOOBAR.repeat(4096/XFOOBAR.len()); - + // header names 1b to 4096b for p in 0..=12 { let n = 1 << p; let payload = [&xfoobar_4k[..n], b": b", RNRN].concat().leak(); _header(c, &format!("name_{}b", n), payload); } - + // header values 1b to 4096b for p in 0..=12 { let n = 1 << p; let payload = [b"a: ", &xfoobar_4k[..n], RNRN].concat().leak(); _header(c, &format!("value_{}b", n), payload); } - + // 1 to 128 for p in 0..=7 { let n = 1 << p; @@ -151,13 +170,11 @@ fn version(c: &mut Criterion) { c.benchmark_group("version") .throughput(Throughput::Bytes(input.len() as u64)) .bench_function(name, |b| b.iter(|| { - black_box({ - let mut b = httparse::_benchable::Bytes::new(input); - httparse::_benchable::parse_version(&mut b).unwrap() - }); + let mut b = httparse::_benchable::Bytes::new(black_box(input)); + httparse::_benchable::parse_version(&mut b).unwrap() })); } - + _version(c, "http10", b"HTTP/1.0\r\n"); _version(c, "http11", b"HTTP/1.1\r\n"); _version(c, "partial", b"HTTP/1."); @@ -168,13 +185,11 @@ fn method(c: &mut Criterion) { c.benchmark_group("method") .throughput(Throughput::Bytes(input.len() as u64)) .bench_function(name, |b| b.iter(|| { - black_box({ - let mut b = httparse::_benchable::Bytes::new(input); - httparse::_benchable::parse_method(&mut b).unwrap() - }); + let mut b = httparse::_benchable::Bytes::new(black_box(input)); + httparse::_benchable::parse_method(&mut b).unwrap() })); } - + // Common methods should be fast-pathed const COMMON_METHODS: &[&str] = &["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"]; for method in COMMON_METHODS {