When transfer data from tcp socket to file directly, splice will help us with low time cost in linux. Currently, os.File just uses write to transfer data from tcp socket.

To do this in linux, there are some changes: 1. add LimitWriter to detect writing limit in internal package, discussed in this issues. 2. add spliceW function to splice data from tcp socket to file. 3. add WriteTo function for net.TCPConn.

We get 10% at least cpu time reduce, zero memory copy from kernel to user space.

Golang net: add tcp WriteTo func to enable splice socket data to file

Implement PR: #46149, go review

Comment From: rsc

What are the specific API changes you are proposing?

Comment From: rsc

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings. — rsc for the proposal review group

Comment From: rsc

It looks like there are no direct API changes other than TCPConn implementing io.WriterTo by adding a WriteTo method. This means that any io.Copy(dst, src) where src is a TCPConn and dst has a ReadFrom will now use src.WriteTo instead of dst.ReadFrom. Are there are any cases where that matters? If dst and src are both networks, presumably we end up at splice either way. But are there other kinds of dsts that matter?

If splice isn't applicable to this specific TCPConn, we fall back to io.Copy(dst, noWriteTo{src}), so we will still get to the dst.ReadFrom.

So the only possible change is when splice is an option but not the best option. Does that case exist?

Comment From: rsc

If we do find such a case, TCPConn.WriteTo can always not use splice then.

Comment From: rsc

Based on the discussion above, this proposal seems like a likely accept. — rsc for the proposal review group

Comment From: jim3ma

It looks like there are no direct API changes other than TCPConn implementing io.WriterTo by adding a WriteTo method. This means that any io.Copy(dst, src) where src is a TCPConn and dst has a ReadFrom will now use src.WriteTo instead of dst.ReadFrom. Are there are any cases where that matters? If dst and src are both networks, presumably we end up at splice either way. But are there other kinds of dsts that matter?

If splice isn't applicable to this specific TCPConn, we fall back to io.Copy(dst, noWriteTo{src}), so we will still get to the dst.ReadFrom.

So the only possible change is when splice is an option but not the best option. Does that case exist?

Currently, I just find two cases: * From net.TCPConn to os.File * From net.TCPConn to net.TCPConn

Two cases has been handled in https://go-review.googlesource.com/c/go/+/319593

Comment From: rsc

No change in consensus, so accepted. 🎉 This issue now tracks the work of implementing the proposal. — rsc for the proposal review group

Comment From: jim3ma

Optimized http get code based on enable splice socket data to file: https://github.com/jim3ma/go/tree/dev.http.writeto.1.16-2021-11-28

Typical testing result(download 1 Gigabytes using http.Get):

echo 3 > /proc/sys/vm/drop_caches; time ./httpget-optmized; rm -f test.output;
copied bytes: 1048576000
./httpget-optmized  0.01s user 0.29s system 74% cpu 0.403 total

echo 3 > /proc/sys/vm/drop_caches; time ./httpget-no-optmized; rm -f test.output;
copied bytes: 1048576000
./httpget-no-optmized  0.02s user 0.39s system 94% cpu 0.436 total

testing code:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:3000/test.data", nil)
    if err != nil {
        panic(err)
    }
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    f, err := os.OpenFile("test.output", os.O_CREATE|os.O_RDWR, 0644)
    if err != nil {
        panic(err)
    }
    n, err := io.Copy(f, resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Printf("copied bytes: %d\n", n)
}

Comment From: totallyunknown

Is there anything missing in the CL? I am also interested in this feature since this will speed up writes from TCP sockets to files.

Comment From: jim3ma

Is there anything missing in the CL? I am also interested in this feature since this will speed up writes from TCP sockets to files.

Nothing, I think that CL is ready.

Comment From: costela

hi @jim3ma! any chance you can rebase your CL? Maybe we can get the ball rolling again? :crossed_fingers:

Comment From: ignoramous

hi @jim3ma! any chance you can rebase your CL

Think this is done: https://github.com/golang/go/issues/58808