Greatly improve performance for TcpSocketBuf.read
For every call to the read() function the internal buffer was copied into a new buffer (minus the bytes copied into the result buffer). When the internal buffer is large enough, this severely affects performance, especially when read_line() is used which calls read_byte() (which calls read()) for each read byte. For line oriented I/O this wasn't all that bad, because the internal buffers usually never were very big. The effect is much more visible once the buffer grows larger. Now we always first look into the internal buffer and copy as many bytes as possible (and desired) into the result buffer. If we need more, we call the socket read function and use the result as the new internal buffer, then continue to copy from the (new) internal buffer, and so on.
This commit is contained in:
parent
0c3ef3cc6b
commit
1ecdf3abc1
1 changed files with 78 additions and 36 deletions
|
@ -72,7 +72,7 @@ pub fn TcpSocket(socket_data: @TcpSocketData) -> TcpSocket {
|
|||
*/
|
||||
pub struct TcpSocketBuf {
|
||||
data: @TcpBufferedSocketData,
|
||||
mut end_of_stream: bool,
|
||||
mut end_of_stream: bool
|
||||
}
|
||||
|
||||
pub fn TcpSocketBuf(data: @TcpBufferedSocketData) -> TcpSocketBuf {
|
||||
|
@ -809,7 +809,7 @@ fn listen_common(host_ip: ip::IpAddr, port: uint, backlog: uint,
|
|||
* A buffered wrapper that you can cast as an `io::reader` or `io::writer`
|
||||
*/
|
||||
pub fn socket_buf(sock: TcpSocket) -> TcpSocketBuf {
|
||||
TcpSocketBuf(@TcpBufferedSocketData { sock: sock, buf: ~[] })
|
||||
TcpSocketBuf(@TcpBufferedSocketData { sock: move sock, mut buf: ~[], buf_off: 0 })
|
||||
}
|
||||
|
||||
/// Convenience methods extending `net::tcp::tcp_socket`
|
||||
|
@ -859,8 +859,40 @@ impl TcpSocket {
|
|||
/// Implementation of `io::reader` trait for a buffered `net::tcp::tcp_socket`
|
||||
impl TcpSocketBuf: io::Reader {
|
||||
fn read(&self, buf: &[mut u8], len: uint) -> uint {
|
||||
// Loop until our buffer has enough data in it for us to read from.
|
||||
while self.data.buf.len() < len {
|
||||
if len == 0 { return 0 }
|
||||
let mut count: uint = 0;
|
||||
|
||||
loop {
|
||||
assert count < len;
|
||||
|
||||
// If possible, copy up to `len` bytes from the internal
|
||||
// `data.buf` into `buf`
|
||||
let nbuffered = self.data.buf.len() - self.data.buf_off;
|
||||
let needed = len - count;
|
||||
if nbuffered > 0 {
|
||||
unsafe {
|
||||
let ncopy = uint::min(nbuffered, needed);
|
||||
let dst = ptr::mut_offset(
|
||||
vec::raw::to_mut_ptr(buf), count);
|
||||
let src = ptr::const_offset(
|
||||
vec::raw::to_const_ptr(self.data.buf),
|
||||
self.data.buf_off);
|
||||
ptr::copy_memory(dst, src, ncopy);
|
||||
self.data.buf_off += ncopy;
|
||||
count += ncopy;
|
||||
}
|
||||
}
|
||||
|
||||
assert count <= len;
|
||||
if count == len {
|
||||
break;
|
||||
}
|
||||
|
||||
// We copied all the bytes we had in the internal buffer into
|
||||
// the result buffer, but the caller wants more bytes, so we
|
||||
// need to read in data from the socket. Note that the internal
|
||||
// buffer is of no use anymore as we read all bytes from it,
|
||||
// so we can throw it away.
|
||||
let read_result = read(&self.data.sock, 0u);
|
||||
if read_result.is_err() {
|
||||
let err_data = read_result.get_err();
|
||||
|
@ -871,36 +903,45 @@ impl TcpSocketBuf: io::Reader {
|
|||
} else {
|
||||
debug!("ERROR sock_buf as io::reader.read err %? %?",
|
||||
err_data.err_name, err_data.err_msg);
|
||||
|
||||
return 0;
|
||||
// As we have already copied data into result buffer,
|
||||
// we cannot simply return 0 here. Instead the error
|
||||
// should show up in a later call to read().
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.data.buf.push_all(result::unwrap(read_result));
|
||||
self.data.buf = result::unwrap(read_result);
|
||||
self.data.buf_off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let count = uint::min(len, self.data.buf.len());
|
||||
|
||||
let mut data = ~[];
|
||||
self.data.buf <-> data;
|
||||
|
||||
vec::bytes::copy_memory(buf, vec::view(data, 0, data.len()), count);
|
||||
|
||||
self.data.buf.push_all(vec::view(data, count, data.len()));
|
||||
|
||||
count
|
||||
}
|
||||
fn read_byte(&self) -> int {
|
||||
let mut bytes = ~[0];
|
||||
if self.read(bytes, 1u) == 0 {
|
||||
if self.end_of_stream {
|
||||
-1
|
||||
loop {
|
||||
if self.data.buf.len() > self.data.buf_off {
|
||||
let c = self.data.buf[self.data.buf_off];
|
||||
self.data.buf_off += 1;
|
||||
return c as int
|
||||
}
|
||||
|
||||
let read_result = read(&self.data.sock, 0u);
|
||||
if read_result.is_err() {
|
||||
let err_data = read_result.get_err();
|
||||
|
||||
if err_data.err_name == ~"EOF" {
|
||||
self.end_of_stream = true;
|
||||
return -1
|
||||
} else {
|
||||
debug!("ERROR sock_buf as io::reader.read err %? %?",
|
||||
err_data.err_name, err_data.err_msg);
|
||||
fail
|
||||
}
|
||||
} else {
|
||||
bytes[0] as int
|
||||
}
|
||||
else {
|
||||
self.data.buf = result::unwrap(read_result);
|
||||
self.data.buf_off = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn eof(&self) -> bool {
|
||||
|
@ -1375,6 +1416,7 @@ struct TcpSocketData {
|
|||
struct TcpBufferedSocketData {
|
||||
sock: TcpSocket,
|
||||
mut buf: ~[u8],
|
||||
mut buf_off: uint
|
||||
}
|
||||
|
||||
//#[cfg(test)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue