Skip to content

Commit 6f6ff10

Browse files
authored
Switch benches to criterion and remove pico targets (#112)
Criterion allows us to crank up the sampling size and confidence, which is important for these kinds of microbenchmarks that can have high variance. It also has other bells and whistles like tracking relative performance between runs, which can be very helpful for development. Per discussion in Discord, the presence of pico in these benchmarks has outlived its usefulness; we're now primarily concerned with the relative performance of httparse between changes.
1 parent 8dbca74 commit 6f6ff10

File tree

2 files changed

+67
-89
lines changed

2 files changed

+67
-89
lines changed

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ default = ["std"]
1818
std = []
1919

2020
[dev-dependencies]
21-
pico-sys = "0.0.1"
21+
criterion = "0.3.5"
22+
23+
[lib]
24+
bench = false
25+
26+
[[bench]]
27+
name = "parse"
28+
harness = false
2229

2330
[profile.bench]
2431
lto = true

benches/parse.rs

Lines changed: 59 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#![feature(test)]
2-
3-
extern crate pico_sys as pico;
1+
extern crate criterion;
42
extern crate httparse;
53

6-
extern crate test;
4+
use std::time::Duration;
5+
6+
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
77

88
const REQ_SHORT: &'static [u8] = b"\
99
GET / HTTP/1.0\r\n\
@@ -22,101 +22,72 @@ Keep-Alive: 115\r\n\
2222
Connection: keep-alive\r\n\
2323
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";
2424

25+
fn req(c: &mut Criterion) {
26+
let mut headers = [httparse::Header{ name: "", value: &[] }; 16];
27+
let mut req = httparse::Request::new(&mut headers);
2528

26-
#[bench]
27-
fn bench_pico(b: &mut test::Bencher) {
28-
use std::mem;
29-
30-
#[repr(C)]
31-
#[derive(Clone, Copy)]
32-
struct Header<'a>(&'a [u8], &'a [u8]);
33-
34-
35-
#[repr(C)]
36-
struct Headers<'a>(&'a mut [Header<'a>]);
37-
let method = [0i8; 16];
38-
let path = [0i8; 16];
39-
let mut minor_version = 0;
40-
let mut h = [Header(&[], &[]); 16];
41-
let mut h_len = h.len();
42-
let headers = Headers(&mut h);
43-
let prev_buf_len = 0;
44-
45-
b.iter(|| {
46-
let ret = unsafe {
47-
pico::ffi::phr_parse_request(
48-
REQ.as_ptr() as *const _,
49-
REQ.len(),
50-
&mut method.as_ptr(),
51-
&mut 16,
52-
&mut path.as_ptr(),
53-
&mut 16,
54-
&mut minor_version,
55-
mem::transmute::<*mut Header, *mut pico::ffi::phr_header>(headers.0.as_mut_ptr()),
56-
&mut h_len as *mut usize as *mut _,
57-
prev_buf_len
58-
)
59-
};
60-
assert_eq!(ret, REQ.len() as i32);
61-
});
62-
b.bytes = REQ.len() as u64;
29+
c.benchmark_group("req")
30+
.throughput(Throughput::Bytes(REQ.len() as u64))
31+
.bench_function("req", |b| b.iter(|| {
32+
assert_eq!(black_box(req.parse(REQ).unwrap()), httparse::Status::Complete(REQ.len()));
33+
}));
6334
}
6435

65-
#[bench]
66-
fn bench_httparse(b: &mut test::Bencher) {
36+
fn req_short(c: &mut Criterion) {
6737
let mut headers = [httparse::Header{ name: "", value: &[] }; 16];
6838
let mut req = httparse::Request::new(&mut headers);
69-
b.iter(|| {
70-
assert_eq!(req.parse(REQ).unwrap(), httparse::Status::Complete(REQ.len()));
71-
});
72-
b.bytes = REQ.len() as u64;
73-
}
7439

75-
#[bench]
76-
fn bench_pico_short(b: &mut test::Bencher) {
77-
use std::mem;
40+
c.benchmark_group("req_short")
41+
.throughput(Throughput::Bytes(REQ_SHORT.len() as u64))
42+
.bench_function("req_short", |b| b.iter(|| {
43+
assert_eq!(black_box(req.parse(REQ_SHORT).unwrap()), httparse::Status::Complete(REQ_SHORT.len()));
44+
}));
45+
}
7846

79-
#[repr(C)]
80-
#[derive(Clone, Copy)]
81-
struct Header<'a>(&'a [u8], &'a [u8]);
47+
const RESP_SHORT: &'static [u8] = b"\
48+
HTTP/1.0 200 OK\r\n\
49+
Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
50+
Set-Cookie: session=60; user_id=1\r\n\r\n";
8251

52+
// These particular headers don't all make semantic sense for a response, but they're syntactically valid.
53+
const RESP: &'static [u8] = b"\
54+
HTTP/1.1 200 OK\r\n\
55+
Date: Wed, 21 Oct 2015 07:28:00 GMT\r\n\
56+
Host: www.kittyhell.com\r\n\
57+
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9\r\n\
58+
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n\
59+
Accept-Language: ja,en-us;q=0.7,en;q=0.3\r\n\
60+
Accept-Encoding: gzip,deflate\r\n\
61+
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7\r\n\
62+
Keep-Alive: 115\r\n\
63+
Connection: keep-alive\r\n\
64+
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";
8365

84-
#[repr(C)]
85-
struct Headers<'a>(&'a mut [Header<'a>]);
86-
let method = [0i8; 16];
87-
let path = [0i8; 16];
88-
let mut minor_version = 0;
89-
let mut h = [Header(&[], &[]); 16];
90-
let mut h_len = h.len();
91-
let headers = Headers(&mut h);
92-
let prev_buf_len = 0;
66+
fn resp(c: &mut Criterion) {
67+
let mut headers = [httparse::Header{ name: "", value: &[] }; 16];
68+
let mut resp = httparse::Response::new(&mut headers);
9369

94-
b.iter(|| {
95-
let ret = unsafe {
96-
pico::ffi::phr_parse_request(
97-
REQ_SHORT.as_ptr() as *const _,
98-
REQ_SHORT.len(),
99-
&mut method.as_ptr(),
100-
&mut 16,
101-
&mut path.as_ptr(),
102-
&mut 16,
103-
&mut minor_version,
104-
mem::transmute::<*mut Header, *mut pico::ffi::phr_header>(headers.0.as_mut_ptr()),
105-
&mut h_len as *mut usize as *mut _,
106-
prev_buf_len
107-
)
108-
};
109-
assert_eq!(ret, REQ_SHORT.len() as i32);
110-
});
111-
b.bytes = REQ_SHORT.len() as u64;
70+
c.benchmark_group("resp")
71+
.throughput(Throughput::Bytes(RESP.len() as u64))
72+
.bench_function("resp", |b| b.iter(|| {
73+
assert_eq!(black_box(resp.parse(RESP).unwrap()), httparse::Status::Complete(RESP.len()));
74+
}));
11275
}
11376

114-
#[bench]
115-
fn bench_httparse_short(b: &mut test::Bencher) {
77+
fn resp_short(c: &mut Criterion) {
11678
let mut headers = [httparse::Header{ name: "", value: &[] }; 16];
117-
let mut req = httparse::Request::new(&mut headers);
118-
b.iter(|| {
119-
assert_eq!(req.parse(REQ_SHORT).unwrap(), httparse::Status::Complete(REQ_SHORT.len()));
120-
});
121-
b.bytes = REQ_SHORT.len() as u64;
79+
let mut resp = httparse::Response::new(&mut headers);
80+
81+
c.benchmark_group("resp_short")
82+
.throughput(Throughput::Bytes(RESP_SHORT.len() as u64))
83+
.bench_function("resp_short", |b| b.iter(|| {
84+
assert_eq!(black_box(resp.parse(RESP_SHORT).unwrap()), httparse::Status::Complete(RESP_SHORT.len()));
85+
}));
86+
}
87+
88+
criterion_group!{
89+
name = benches;
90+
config = Criterion::default().sample_size(100).measurement_time(Duration::from_secs(10));
91+
targets = req, req_short, resp, resp_short
12292
}
93+
criterion_main!(benches);

0 commit comments

Comments
 (0)