perf(headers): switch from fmt to itoa when writing content-length header
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user