Skip to content

Commit 1ecdf3a

Browse files
mneumannbrson
authored andcommitted
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.
1 parent 0c3ef3c commit 1ecdf3a

File tree

1 file changed

+77
-35
lines changed

1 file changed

+77
-35
lines changed

src/libstd/net_tcp.rs

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub fn TcpSocket(socket_data: @TcpSocketData) -> TcpSocket {
7272
*/
7373
pub struct TcpSocketBuf {
7474
data: @TcpBufferedSocketData,
75-
mut end_of_stream: bool,
75+
mut end_of_stream: bool
7676
}
7777

7878
pub fn TcpSocketBuf(data: @TcpBufferedSocketData) -> TcpSocketBuf {
@@ -809,7 +809,7 @@ fn listen_common(host_ip: ip::IpAddr, port: uint, backlog: uint,
809809
* A buffered wrapper that you can cast as an `io::reader` or `io::writer`
810810
*/
811811
pub fn socket_buf(sock: TcpSocket) -> TcpSocketBuf {
812-
TcpSocketBuf(@TcpBufferedSocketData { sock: sock, buf: ~[] })
812+
TcpSocketBuf(@TcpBufferedSocketData { sock: move sock, mut buf: ~[], buf_off: 0 })
813813
}
814814

815815
/// Convenience methods extending `net::tcp::tcp_socket`
@@ -859,48 +859,89 @@ impl TcpSocket {
859859
/// Implementation of `io::reader` trait for a buffered `net::tcp::tcp_socket`
860860
impl TcpSocketBuf: io::Reader {
861861
fn read(&self, buf: &[mut u8], len: uint) -> uint {
862-
// Loop until our buffer has enough data in it for us to read from.
863-
while self.data.buf.len() < len {
864-
let read_result = read(&self.data.sock, 0u);
865-
if read_result.is_err() {
866-
let err_data = read_result.get_err();
862+
if len == 0 { return 0 }
863+
let mut count: uint = 0;
867864

868-
if err_data.err_name == ~"EOF" {
869-
self.end_of_stream = true;
870-
break;
871-
} else {
872-
debug!("ERROR sock_buf as io::reader.read err %? %?",
873-
err_data.err_name, err_data.err_msg);
865+
loop {
866+
assert count < len;
874867

875-
return 0;
868+
// If possible, copy up to `len` bytes from the internal
869+
// `data.buf` into `buf`
870+
let nbuffered = self.data.buf.len() - self.data.buf_off;
871+
let needed = len - count;
872+
if nbuffered > 0 {
873+
unsafe {
874+
let ncopy = uint::min(nbuffered, needed);
875+
let dst = ptr::mut_offset(
876+
vec::raw::to_mut_ptr(buf), count);
877+
let src = ptr::const_offset(
878+
vec::raw::to_const_ptr(self.data.buf),
879+
self.data.buf_off);
880+
ptr::copy_memory(dst, src, ncopy);
881+
self.data.buf_off += ncopy;
882+
count += ncopy;
876883
}
877-
}
878-
else {
879-
self.data.buf.push_all(result::unwrap(read_result));
880-
}
881-
}
882-
883-
let count = uint::min(len, self.data.buf.len());
884-
885-
let mut data = ~[];
886-
self.data.buf <-> data;
884+
}
887885

888-
vec::bytes::copy_memory(buf, vec::view(data, 0, data.len()), count);
886+
assert count <= len;
887+
if count == len {
888+
break;
889+
}
889890

890-
self.data.buf.push_all(vec::view(data, count, data.len()));
891+
// We copied all the bytes we had in the internal buffer into
892+
// the result buffer, but the caller wants more bytes, so we
893+
// need to read in data from the socket. Note that the internal
894+
// buffer is of no use anymore as we read all bytes from it,
895+
// so we can throw it away.
896+
let read_result = read(&self.data.sock, 0u);
897+
if read_result.is_err() {
898+
let err_data = read_result.get_err();
899+
900+
if err_data.err_name == ~"EOF" {
901+
self.end_of_stream = true;
902+
break;
903+
} else {
904+
debug!("ERROR sock_buf as io::reader.read err %? %?",
905+
err_data.err_name, err_data.err_msg);
906+
// As we have already copied data into result buffer,
907+
// we cannot simply return 0 here. Instead the error
908+
// should show up in a later call to read().
909+
break;
910+
}
911+
}
912+
else {
913+
self.data.buf = result::unwrap(read_result);
914+
self.data.buf_off = 0;
915+
}
916+
}
891917

892918
count
893919
}
894920
fn read_byte(&self) -> int {
895-
let mut bytes = ~[0];
896-
if self.read(bytes, 1u) == 0 {
897-
if self.end_of_stream {
898-
-1
899-
} else {
900-
fail
901-
}
902-
} else {
903-
bytes[0] as int
921+
loop {
922+
if self.data.buf.len() > self.data.buf_off {
923+
let c = self.data.buf[self.data.buf_off];
924+
self.data.buf_off += 1;
925+
return c as int
926+
}
927+
928+
let read_result = read(&self.data.sock, 0u);
929+
if read_result.is_err() {
930+
let err_data = read_result.get_err();
931+
932+
if err_data.err_name == ~"EOF" {
933+
self.end_of_stream = true;
934+
return -1
935+
} else {
936+
debug!("ERROR sock_buf as io::reader.read err %? %?",
937+
err_data.err_name, err_data.err_msg);
938+
fail
939+
}
940+
}
941+
else {
942+
self.data.buf = result::unwrap(read_result);
943+
self.data.buf_off = 0;
944+
}
904945
}
905946
}
906947
fn eof(&self) -> bool {
@@ -1375,6 +1416,7 @@ struct TcpSocketData {
13751416
struct TcpBufferedSocketData {
13761417
sock: TcpSocket,
13771418
mut buf: ~[u8],
1419+
mut buf_off: uint
13781420
}
13791421

13801422
//#[cfg(test)]

0 commit comments

Comments
 (0)