通过引入 DMA,我们已经把 Linux 的 I/O 过程中的 CPU 拷贝次数从 4 次减少到了 2 次,但是 CPU 拷贝依然是代价很大的操作,对系统性能的影响还是很大,特别是那些频繁 I/O 的场景,更是会因为 CPU 拷贝而损失掉很多性能,我们需要进一步优化,降低、甚至是完全避免 CPU 拷贝。
funccopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. if wt, ok := src.(WriterTo); ok { return wt.WriteTo(dst) } // Similarly, if the writer has a ReadFrom method, use it to do the copy. if rt, ok := dst.(ReaderFrom); ok { return rt.ReadFrom(src) } ......
上面的源码中可以看到,两个 type assertion。 现在关注一下第二个 dst.(ReaderFrom)。
// A response represents the server side of an HTTP response. type response struct { conn *conn req *Request // request for this response reqBody io.ReadCloser cancelCtx context.CancelFunc // when ServeHTTP exits wroteHeader bool// reply header has been (logically) written wroteContinue bool// 100 Continue response was written wants10KeepAlive bool// HTTP/1.0 w/ Connection "keep-alive" wantsClose bool// HTTP request has Connection "close"
w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter
// ReadFrom is here to optimize copying from an *os.File regular file // to a *net.TCPConn with sendfile. func(w *response) ReadFrom(src io.Reader) (n int64, err error) { // Our underlying w.conn.rwc is usually a *TCPConn (with its // own ReadFrom method). If not, or if our src isn't a regular // file, just fall back to the normal copy method. rf, ok := w.conn.rwc.(io.ReaderFrom) regFile, err := srcIsRegularFile(src) if err != nil { return0, err } if !ok || !regFile { bufp := copyBufPool.Get().(*[]byte) defer copyBufPool.Put(bufp) return io.CopyBuffer(writerOnly{w}, src, *bufp) }
// sendfile path:
if !w.wroteHeader { w.WriteHeader(StatusOK) }
if w.needsSniff() { n0, err := io.Copy(writerOnly{w}, io.LimitReader(src, sniffLen)) n += n0 if err != nil { return n, err } }
w.w.Flush() // get rid of any previous writes w.cw.flush() // make sure Header is written; flush data to rwc
// Now that cw has been flushed, its chunking field is guaranteed initialized. if !w.cw.chunking && w.bodyAllowed() { n0, err := rf.ReadFrom(src) n += n0 w.written += n0 return n, err }
n0, err := io.Copy(writerOnly{w}, src) n += n0 return n, err }
函数开头有一段注释已经说明调用的底层是 *TCPConn 的 ReadFrom。
1 2 3 4 5 6 7 8 9 10 11
// ReadFrom implements the io.ReaderFrom ReadFrom method. func(c *TCPConn) ReadFrom(r io.Reader) (int64, error) { if !c.ok() { return0, syscall.EINVAL } n, err := c.readFrom(r) if err != nil && err != io.EOF { err = &OpError{Op: "readfrom", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} } return n, err }
终于出现了 splice() 和 sendFile()
1 2 3 4 5 6 7 8 9
func(c *TCPConn) readFrom(r io.Reader) (int64, error) { if n, err, handled := splice(c.fd, r); handled { return n, err } if n, err, handled := sendFile(c.fd, r); handled { return n, err } return genericReadFrom(c, r) }