perf(headers): switch from fmt to itoa when writing content-length header

This commit is contained in:
Sean McArthur
2018-06-20 17:19:37 -07:00
parent 7d8897537b
commit ac1af8d15b
4 changed files with 46 additions and 7 deletions

View File

@@ -26,6 +26,7 @@ http = "0.1.5"
httparse = "1.0" httparse = "1.0"
h2 = "0.1.5" h2 = "0.1.5"
iovec = "0.1" iovec = "0.1"
itoa = "0.4.1"
log = "0.4" log = "0.4"
net2 = { version = "0.2.32", optional = true } net2 = { version = "0.2.32", optional = true }
time = "0.1" time = "0.1"

View File

@@ -1,5 +1,3 @@
use std::fmt::Write;
use bytes::BytesMut; use bytes::BytesMut;
use http::HeaderMap; use http::HeaderMap;
use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING}; use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
@@ -70,7 +68,7 @@ pub fn content_length_parse_all_values(values: ValueIter<HeaderValue>) -> Option
pub fn content_length_value(len: u64) -> HeaderValue { pub fn content_length_value(len: u64) -> HeaderValue {
let mut len_buf = BytesMut::with_capacity(MAX_DECIMAL_U64_BYTES); let mut len_buf = BytesMut::with_capacity(MAX_DECIMAL_U64_BYTES);
write!(len_buf, "{}", len) ::itoa::fmt(&mut len_buf, len)
.expect("BytesMut can hold a decimal u64"); .expect("BytesMut can hold a decimal u64");
// safe because u64 Display is ascii numerals // safe because u64 Display is ascii numerals
unsafe { unsafe {
@@ -141,6 +139,10 @@ fn eq_ascii(left: &str, right: &str) -> bool {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[cfg(feature = "nightly")]
use test::Bencher;
#[test] #[test]
fn assert_max_decimal_u64_bytes() { fn assert_max_decimal_u64_bytes() {
assert_eq!( assert_eq!(
@@ -148,4 +150,32 @@ mod tests {
::std::u64::MAX.to_string().len() ::std::u64::MAX.to_string().len()
); );
} }
#[cfg(feature = "nightly")]
#[bench]
fn bench_content_length_fmt_small(b: &mut Bencher) {
let n = 13;
let s = n.to_string();
b.bytes = s.len() as u64;
b.iter(|| {
let val = super::content_length_value(n);
debug_assert_eq!(val, s);
::test::black_box(&val);
})
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_content_length_fmt_big(b: &mut Bencher) {
let n = 326_893_010;
let s = n.to_string();
b.bytes = s.len() as u64;
b.iter(|| {
let val = super::content_length_value(n);
debug_assert_eq!(val, s);
::test::black_box(&val);
})
}
} }

View File

@@ -21,6 +21,7 @@ extern crate h2;
extern crate http; extern crate http;
extern crate httparse; extern crate httparse;
extern crate iovec; extern crate iovec;
extern crate itoa;
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[cfg(feature = "runtime")] extern crate net2; #[cfg(feature = "runtime")] extern crate net2;
extern crate time; extern crate time;

View File

@@ -190,7 +190,7 @@ impl Http1Transaction for Server {
})) }))
} }
fn encode(mut msg: Encode<Self::Outgoing>, dst: &mut Vec<u8>) -> ::Result<Encoder> { fn encode(mut msg: Encode<Self::Outgoing>, mut dst: &mut Vec<u8>) -> ::Result<Encoder> {
trace!( trace!(
"Server::encode status={:?}, body={:?}, req_method={:?}", "Server::encode status={:?}, body={:?}, req_method={:?}",
msg.head.subject, msg.head.subject,
@@ -439,7 +439,9 @@ impl Http1Transaction for Server {
Encoder::length(0) Encoder::length(0)
}, },
Some(BodyLength::Known(len)) => { Some(BodyLength::Known(len)) => {
let _ = write!(FastWrite(dst), "content-length: {}\r\n", len); extend(dst, b"content-length: ");
let _ = ::itoa::write(&mut dst, len);
extend(dst, b"\r\n");
Encoder::length(len) Encoder::length(len)
}, },
}; };
@@ -1444,9 +1446,9 @@ mod tests {
b.bytes = len as u64; b.bytes = len as u64;
let mut head = MessageHead::default(); let mut head = MessageHead::default();
let mut vec = Vec::with_capacity(128);
b.iter(|| { b.iter(|| {
let mut vec = Vec::new();
Server::encode(Encode { Server::encode(Encode {
head: &mut head, head: &mut head,
body: Some(BodyLength::Known(10)), body: Some(BodyLength::Known(10)),
@@ -1455,7 +1457,12 @@ mod tests {
title_case_headers: false, title_case_headers: false,
}, &mut vec).unwrap(); }, &mut vec).unwrap();
assert_eq!(vec.len(), len); assert_eq!(vec.len(), len);
::test::black_box(vec); ::test::black_box(&vec);
// reset Vec<u8> to 0 (always safe)
unsafe {
vec.set_len(0);
}
}) })
} }
} }