Skip to content

Commit

Permalink
perf(server): cache renderings of the Date header
Browse files Browse the repository at this point in the history
This is actually one of the biggest impacts to benchmark performances at
this point. Caching the rendering of the Date header improves "hello
world" benchmarks by around 10%.
  • Loading branch information
seanmonstar committed May 26, 2017
1 parent 78a8eed commit bdd2e1a
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 17 deletions.
59 changes: 59 additions & 0 deletions src/http/h1/date.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::cell::RefCell;
use std::fmt::{self, Write};
use std::str;

use time::{self, Duration};

// "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29;

pub fn extend(dst: &mut Vec<u8>) {
CACHED.with(|cache| {
let mut cache = cache.borrow_mut();
let now = time::get_time();
if now > cache.next_update {
cache.update(now);
}
dst.extend_from_slice(cache.buffer());
})
}

struct CachedDate {
bytes: [u8; DATE_VALUE_LENGTH],
pos: usize,
next_update: time::Timespec,
}

thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate {
bytes: [0; DATE_VALUE_LENGTH],
pos: 0,
next_update: time::Timespec::new(0, 0),
}));

impl CachedDate {
fn buffer(&self) -> &[u8] {
&self.bytes[..]
}

fn update(&mut self, now: time::Timespec) {
self.pos = 0;
write!(self, "{}", time::at_utc(now).rfc822()).unwrap();
assert!(self.pos == DATE_VALUE_LENGTH);
self.next_update = now + Duration::seconds(1);
self.next_update.nsec = 0;
}
}

impl fmt::Write for CachedDate {
fn write_str(&mut self, s: &str) -> fmt::Result {
let len = s.len();
self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
self.pos += len;
Ok(())
}
}

#[test]
fn test_date_len() {
assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
}
1 change: 1 addition & 0 deletions src/http/h1/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use self::decode::Decoder;
pub use self::encode::Encoder;

mod date;
mod decode;
mod encode;
pub mod parse;
Expand Down
31 changes: 14 additions & 17 deletions src/http/h1/parse.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::borrow::Cow;
use std::fmt::{self, Write};
use std::time::SystemTime;

use httparse;
use bytes::{BytesMut, Bytes};

use header::{self, Headers, ContentLength, TransferEncoding};
use http::{ByteStr, MessageHead, RawStatus, Http1Transaction, ParseResult, ServerTransaction, ClientTransaction, RequestLine};
use http::h1::{Encoder, Decoder};
use http::h1::{Encoder, Decoder, date};
use method::Method;
use status::StatusCode;
use version::HttpVersion::{Http10, Http11};
Expand Down Expand Up @@ -90,10 +89,6 @@ impl Http1Transaction for ServerTransaction {
use ::header;
trace!("writing head: {:?}", head);

if !head.headers.has::<header::Date>() {
head.headers.set(header::Date(SystemTime::now().into()));
}

let len = head.headers.get::<header::ContentLength>().map(|n| **n);

let body = if let Some(len) = len {
Expand Down Expand Up @@ -121,10 +116,19 @@ impl Http1Transaction for ServerTransaction {
debug!("writing headers = {:?}", head.headers);
if head.version == ::HttpVersion::Http11 && head.subject == ::StatusCode::Ok {
extend(dst, b"HTTP/1.1 200 OK\r\n");
let _ = write!(FastWrite(dst), "{}\r\n", head.headers);
let _ = write!(FastWrite(dst), "{}", head.headers);
} else {
let _ = write!(FastWrite(dst), "{} {}\r\n{}\r\n", head.version, head.subject, head.headers);
let _ = write!(FastWrite(dst), "{} {}\r\n{}", head.version, head.subject, head.headers);
}
// using http::h1::date is quite a lot faster than generating a unique Date header each time
// like req/s goes up about 10%
if !head.headers.has::<header::Date>() {
dst.reserve(date::DATE_VALUE_LENGTH + 8);
extend(dst, b"Date: ");
date::extend(dst);
extend(dst, b"\r\n");
}
extend(dst, b"\r\n");
body
}

Expand Down Expand Up @@ -316,16 +320,9 @@ impl<'a> fmt::Write for FastWrite<'a> {
}
}

#[inline]
fn extend(dst: &mut Vec<u8>, data: &[u8]) {
use std::ptr;
dst.reserve(data.len());
let prev = dst.len();
unsafe {
ptr::copy_nonoverlapping(data.as_ptr(),
dst.as_mut_ptr().offset(prev as isize),
data.len());
dst.set_len(prev + data.len());
}
dst.extend_from_slice(data);
}

#[cfg(test)]
Expand Down

0 comments on commit bdd2e1a

Please sign in to comment.