Hi
I'm new to Rust and have a software that i want to port to Rust for many reasons.
But, I have a performance problem with the way I serve large files using Hyper and tokio.
The current software is twice as fast as my Rust code.
I serve each file as Stream, as it may be to big to fit in a single HTTP response, using Body::wrap_stream
. So I implement a Stream object like that :
struct TokioStream {
file: tokio::fs::File,
buf: Vec<u8>,
}
impl Stream for TokioStream {
type Item = Vec<u8>;
type Error = Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self.file.poll_read(&mut self.buf) {
Ok(Async::Ready(0)) => {
Ok(Async::Ready(None))
},
Ok(Async::Ready(_size)) => {
Ok(Async::Ready(Some(Vec::from(&self.buf[..]))))
},
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(_) => Err(Error::new(ErrorKind::Other, "😭")),
}
}
}
I'm afraid of reallocating a new vector each time poll
is called. How could I use the same vector for every chunk ?
Another point who can explain the performance problem is how I use Tokio and Hyper. But nothing fancy here IMHO :
fn main() {
let addr = "[::1]:1337".parse().unwrap();
let service = || {
service_fn_ok( |_| {
let task = tokio::fs::File::open("big")
.and_then( |file| {
Ok(TokioStream{ file: file,
buf: vec!(0; BUF_SIZE)
})
});
match task.wait() {
Err(_) => Response::new(Body::from("Please (╥_╥)")),
Ok(s) => Response::new(Body::wrap_stream(s)),
}
})
};
let server = Server::bind(&addr)
.serve(service)
.map_err(|e| eprintln!("Error: {}", e));
hyper::rt::run(server);
}
The last thing that I think may be the source of performance problem is the size of the buffer. But after some tests, it seems that 1<<22
is a good size on my machine. I don't know which one is use one the other solution.
const BUF_SIZE : usize = 1<<22;
How can I improve that code ? I'm pretty sure, I can do better with Rust.
To benchmark the solution, I simply download a file to /dev/null
with wget "http://localhost:1337" -0 /dev/null
. The file is about 7Gio big.
$ wget "http://localhost:8080/big" -O /dev/null # The old one
2019-04-20 08:30:12 (420 MB/s) — « /dev/null » sauvegardé [7038594877/7038594877]
$ wget "http://localhost:1337/big" -O /dev/null # The Rust code
2019-04-20 09:10:45 (230 MB/s) — « /dev/null » sauvegardé [7038594877/7038594877]
Any help will be appreciated.
Have a nice day.