Skip to content

Commit

Permalink
perf(http): utilize writev when possible
Browse files Browse the repository at this point in the history
By using `AsyncWrite::write_buf`, we can avoid some copies in some
cases. This especially helps throughput for chunked encoding.
  • Loading branch information
seanmonstar committed Jan 25, 2018
1 parent 11b49c2 commit 68377ed
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 489 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ futures = "0.1.17"
futures-cpupool = "0.1.6"
http = { version = "0.1", optional = true }
httparse = "1.0"
iovec = "0.1"
language-tags = "0.2"
log = "0.4"
mime = "0.3.2"
Expand Down
4 changes: 3 additions & 1 deletion benches/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

extern crate futures;
extern crate hyper;
extern crate pretty_env_logger;
extern crate test;

use std::io::{Read, Write};
Expand All @@ -17,6 +18,7 @@ use hyper::server::{self, Service};

macro_rules! bench_server {
($b:ident, $header:expr, $body:expr) => ({
let _ = pretty_env_logger::try_init();
let (_until_tx, until_rx) = oneshot::channel();
let addr = {
let (addr_tx, addr_rx) = mpsc::channel();
Expand Down Expand Up @@ -53,7 +55,7 @@ macro_rules! bench_server {
sum += tcp.read(&mut buf).unwrap();
}
assert_eq!(sum, total_bytes);
})
});
})
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern crate futures_cpupool;
#[cfg(feature = "compat")]
extern crate http;
extern crate httparse;
extern crate iovec;
extern crate language_tags;
#[macro_use] extern crate log;
pub extern crate mime;
Expand Down
53 changes: 53 additions & 0 deletions src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ impl ::std::ops::Deref for Buf {
}
}

impl AsRef<[u8]> for Buf {
fn as_ref(&self) -> &[u8] {
&self.vec
}
}

impl<S: AsRef<[u8]>> PartialEq<S> for Buf {
fn eq(&self, other: &S) -> bool {
self.vec == other.as_ref()
Expand Down Expand Up @@ -110,6 +116,13 @@ impl AsyncIo<Buf> {
}
}

impl<S: AsRef<[u8]>, T: AsRef<[u8]>> PartialEq<S> for AsyncIo<T> {
fn eq(&self, other: &S) -> bool {
self.inner.as_ref() == other.as_ref()
}
}


impl<T: Read> Read for AsyncIo<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.blocked = false;
Expand Down Expand Up @@ -156,6 +169,46 @@ impl<T: Read + Write> AsyncWrite for AsyncIo<T> {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(().into())
}

fn write_buf<B: ::bytes::Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
use futures::Async;
let r = {
static DUMMY: &[u8] = &[0];
let mut bufs = [From::from(DUMMY); 64];
let i = ::bytes::Buf::bytes_vec(&buf, &mut bufs);
let mut n = 0;
let mut ret = Ok(0);
for iovec in &bufs[..i] {
match self.write(iovec) {
Ok(num) => {
n += num;
ret = Ok(n);
},
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
if let Ok(0) = ret {
ret = Err(e);
}
} else {
ret = Err(e);
}
break;
}
}
}
ret
};
match r {
Ok(n) => {
::bytes::Buf::advance(buf, n);
Ok(Async::Ready(n))
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
Ok(Async::NotReady)
}
Err(e) => Err(e),
}
}
}

impl ::std::ops::Deref for AsyncIo<Buf> {
Expand Down
Loading

0 comments on commit 68377ed

Please sign in to comment.