From ac1af8d15b768f5ba83345ff0af2f3a9a89d4ddf Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 20 Jun 2018 17:19:37 -0700 Subject: [PATCH] perf(headers): switch from fmt to itoa when writing content-length header --- Cargo.toml | 1 + src/headers.rs | 36 +++++++++++++++++++++++++++++++++--- src/lib.rs | 1 + src/proto/h1/role.rs | 15 +++++++++++---- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5bb8e7c..c8b409a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ http = "0.1.5" httparse = "1.0" h2 = "0.1.5" iovec = "0.1" +itoa = "0.4.1" log = "0.4" net2 = { version = "0.2.32", optional = true } time = "0.1" diff --git a/src/headers.rs b/src/headers.rs index 593f9801..fb73bc0f 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -1,5 +1,3 @@ -use std::fmt::Write; - use bytes::BytesMut; use http::HeaderMap; use http::header::{CONTENT_LENGTH, TRANSFER_ENCODING}; @@ -70,7 +68,7 @@ pub fn content_length_parse_all_values(values: ValueIter) -> Option pub fn content_length_value(len: u64) -> HeaderValue { 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"); // safe because u64 Display is ascii numerals unsafe { @@ -141,6 +139,10 @@ fn eq_ascii(left: &str, right: &str) -> bool { #[cfg(test)] mod tests { + + #[cfg(feature = "nightly")] + use test::Bencher; + #[test] fn assert_max_decimal_u64_bytes() { assert_eq!( @@ -148,4 +150,32 @@ mod tests { ::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); + }) + } } diff --git a/src/lib.rs b/src/lib.rs index 05be45b3..5e994eb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ extern crate h2; extern crate http; extern crate httparse; extern crate iovec; +extern crate itoa; #[macro_use] extern crate log; #[cfg(feature = "runtime")] extern crate net2; extern crate time; diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index fb020dc9..20c565f6 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -190,7 +190,7 @@ impl Http1Transaction for Server { })) } - fn encode(mut msg: Encode, dst: &mut Vec) -> ::Result { + fn encode(mut msg: Encode, mut dst: &mut Vec) -> ::Result { trace!( "Server::encode status={:?}, body={:?}, req_method={:?}", msg.head.subject, @@ -439,7 +439,9 @@ impl Http1Transaction for Server { Encoder::length(0) }, 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) }, }; @@ -1444,9 +1446,9 @@ mod tests { b.bytes = len as u64; let mut head = MessageHead::default(); + let mut vec = Vec::with_capacity(128); b.iter(|| { - let mut vec = Vec::new(); Server::encode(Encode { head: &mut head, body: Some(BodyLength::Known(10)), @@ -1455,7 +1457,12 @@ mod tests { title_case_headers: false, }, &mut vec).unwrap(); assert_eq!(vec.len(), len); - ::test::black_box(vec); + ::test::black_box(&vec); + + // reset Vec to 0 (always safe) + unsafe { + vec.set_len(0); + } }) } }