Skip to content

Commit 68377ed

Browse files
committed
perf(http): utilize writev when possible
By using `AsyncWrite::write_buf`, we can avoid some copies in some cases. This especially helps throughput for chunked encoding.
1 parent 11b49c2 commit 68377ed

File tree

10 files changed

+668
-489
lines changed

10 files changed

+668
-489
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ futures = "0.1.17"
2525
futures-cpupool = "0.1.6"
2626
http = { version = "0.1", optional = true }
2727
httparse = "1.0"
28+
iovec = "0.1"
2829
language-tags = "0.2"
2930
log = "0.4"
3031
mime = "0.3.2"

benches/server.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
extern crate futures;
55
extern crate hyper;
6+
extern crate pretty_env_logger;
67
extern crate test;
78

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

1819
macro_rules! bench_server {
1920
($b:ident, $header:expr, $body:expr) => ({
21+
let _ = pretty_env_logger::try_init();
2022
let (_until_tx, until_rx) = oneshot::channel();
2123
let addr = {
2224
let (addr_tx, addr_rx) = mpsc::channel();
@@ -53,7 +55,7 @@ macro_rules! bench_server {
5355
sum += tcp.read(&mut buf).unwrap();
5456
}
5557
assert_eq!(sum, total_bytes);
56-
})
58+
});
5759
})
5860
}
5961

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern crate futures_cpupool;
2424
#[cfg(feature = "compat")]
2525
extern crate http;
2626
extern crate httparse;
27+
extern crate iovec;
2728
extern crate language_tags;
2829
#[macro_use] extern crate log;
2930
pub extern crate mime;

src/mock.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ impl ::std::ops::Deref for Buf {
3131
}
3232
}
3333

34+
impl AsRef<[u8]> for Buf {
35+
fn as_ref(&self) -> &[u8] {
36+
&self.vec
37+
}
38+
}
39+
3440
impl<S: AsRef<[u8]>> PartialEq<S> for Buf {
3541
fn eq(&self, other: &S) -> bool {
3642
self.vec == other.as_ref()
@@ -110,6 +116,13 @@ impl AsyncIo<Buf> {
110116
}
111117
}
112118

119+
impl<S: AsRef<[u8]>, T: AsRef<[u8]>> PartialEq<S> for AsyncIo<T> {
120+
fn eq(&self, other: &S) -> bool {
121+
self.inner.as_ref() == other.as_ref()
122+
}
123+
}
124+
125+
113126
impl<T: Read> Read for AsyncIo<T> {
114127
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
115128
self.blocked = false;
@@ -156,6 +169,46 @@ impl<T: Read + Write> AsyncWrite for AsyncIo<T> {
156169
fn shutdown(&mut self) -> Poll<(), io::Error> {
157170
Ok(().into())
158171
}
172+
173+
fn write_buf<B: ::bytes::Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
174+
use futures::Async;
175+
let r = {
176+
static DUMMY: &[u8] = &[0];
177+
let mut bufs = [From::from(DUMMY); 64];
178+
let i = ::bytes::Buf::bytes_vec(&buf, &mut bufs);
179+
let mut n = 0;
180+
let mut ret = Ok(0);
181+
for iovec in &bufs[..i] {
182+
match self.write(iovec) {
183+
Ok(num) => {
184+
n += num;
185+
ret = Ok(n);
186+
},
187+
Err(e) => {
188+
if e.kind() == io::ErrorKind::WouldBlock {
189+
if let Ok(0) = ret {
190+
ret = Err(e);
191+
}
192+
} else {
193+
ret = Err(e);
194+
}
195+
break;
196+
}
197+
}
198+
}
199+
ret
200+
};
201+
match r {
202+
Ok(n) => {
203+
::bytes::Buf::advance(buf, n);
204+
Ok(Async::Ready(n))
205+
}
206+
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
207+
Ok(Async::NotReady)
208+
}
209+
Err(e) => Err(e),
210+
}
211+
}
159212
}
160213

161214
impl ::std::ops::Deref for AsyncIo<Buf> {

0 commit comments

Comments
 (0)