bufio.Writer.ReadFrom() and bufio.Reader.WriteTo() optimizations

361 views
Skip to first unread message

Aliaksandr Valialkin

unread,
Sep 12, 2013, 11:57:09 AM9/12/13
to golan...@googlegroups.com
Hi all.

The current implementation of bufio.Writer.ReadFrom() shortcuts to b.wr.ReadFrom() call (for example, net.TCPConn) only if the bufio.Writer buffer is empty. Such a behavior has the following drawbacks:
1) Unnesessary calls to b.wr.ReadFrom() if the bufio.Writer buffer is empty and data obtained from the reader fit the buffer.
2) Unnesessary copying to bufio.Writer buffer if it isn't empty and data obtained from the reader doesn't fit the buffer.

b.wr.ReadFrom() should be called only if the first call to r.Read() fills bufio.Writer buffer. This eliminates both drawbacks:
1) b.wr.ReadFrom() won't be called if r.Read() doesn't fill bufio.Writer buffer.
2) reader's data won't be proxied through bufio.Writer buffer if its size exceeds buffer's size - only the first portion of the data, which fills bufio.Writer buffer, will be proxied.

This will speed up the common case when copying data from arbitrary reader which provides big chunk of data (for example, video file) to buffered net.TCPConn by reducing the number of calls to net.TCPConn.ReadFrom() (which result in at least one syscall) and by shortcuting the reader directly to net.TCPConn.ReadFrom() and thus avoiding unnecessary data proxying through small bufio.Writer buffer. Moreover, if the reader is os.File, then net.TCPConn.ReadFrom() is smart enough to shortcut directly to sendfile() syscall. See this topic for details.

The current implementation of bufio.Reader.WriteTo() shortcuts only to b.rd.WriteTo(). But it can do better! Suppose the common case - we read big chunk of data (for example, file) from buffered net.TCPConn to arbitrary writer, which implements io.ReadFrom(). Currently bufio.Reader.WriteTo() falls back to proxying the data through its' internal buffer, because net.TCPConn doesn't implement io.WriterTo interface. It would be great if bufio.Reader.WriteTo() shortcuts to w.ReadFrom(net.TCPConn) in this case, which could result in net.TCPConn.Read(w.internalBuf) call under the hoods of w.ReadFrom() thus avoiding unnecessary data proxying through bufio.Reader buffer.

Suppose another common case, which benefits simultaneously from both optimizations - io.Copy(bufio.Writer{net.TCPConn}, bufio.Reader{os.File}). This unrolls to the following call stack:
  • io.Copy(bufio.Writer{net.TCPConn}, bufio.Reader{os.File}) -> bufio.Writer.ReadFrom(bufio.Reader{os.File}), since bufio.Writer implements ReaderFrom. Currently this will be the last call in the stack if bufio.Writer's buffer isn't empty. For example, it already contains HTTP headers. In this case file's contents will be proxied through the bufio.Writer buffer. buffer.Writer.ReadFrom() optimization mentioned above will lead to the following steps after small portion of file is proxied only once through the bufio.Writer buffer.
  • bufio.Writer.ReadFrom(bufio.Reader{os.File}) -> net.TCPConn.ReadFrom{bufio.Reader{os.File}), since net.TCPConn implements ReaderFrom.
  • net.TCPConn.ReadFrom(bufio.Reader{os.File}) -> io.Copy(writerOnly{net.TCPConn}, bufio.Reader{os.File}), since bufio.Reader doesn't implement neither io.LimitReader nor os.File, thus cannot be shortcutted to sendfile() syscall at this step. But look below!
  • io.Copy(writerOnly{net.TCPConn}, bufio.Reader{os.File}) -> bufio.Reader.WriteTo(net.TCPConn), since writerOnly{net.TCPConn} intentionally hides net.TCPConn.ReadFrom and bufio.Reader implements WriterTo. Currently this is the last step in the stack until the aforementioned shortcut to w.ReadFrom() is implemented.
  • bufio.Reader.WriteTo(net.TCPConn) -> net.TCPConn.ReadFrom(os.File), since net.TCPConn implements ReaderFrom.
  • net.TCPConn.ReadFrom(os.File) -> sendfile() syscall. WOW! Magic!

Aliaksandr Valialkin

unread,
Sep 12, 2013, 12:02:04 PM9/12/13
to golan...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages