-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net: use splice for TCPConn.ReadFrom on Linux #10948
Comments
splice
for net.sendFile
(on Linux)splice
for net.Conn.ReadFrom(net.Conn)
on Linux
Can you provide some benchmark numbers to validate this. In the past the On Tue, 26 May 2015 08:51 Philip Hofer notifications@github.com wrote:
|
please wait until Go 1.5 is out and Go 1.6 window opens
and then we can have a discussion on the mailing list.
|
splice
for net.Conn.ReadFrom(net.Conn)
on Linuxsplice
for net.Conn.ReadFrom(net.Conn)
on Linux
My impression is that the splice system call won't do the right thing with non-blocking network connections. It will simply return EAGAIN. I'd be happy to hear otherwise. If we can get better performance by transparently using splice on GNU/Linux, then we should try to do it for 1.6. |
@ianlancetaylor I have a working prototype here that doesn't seem to have that issue. @minux Ok. @davecheney Yeah, it's going to take a little while to write an appropriate benchmark. Also, some of the performance is destined to be NIC-dependent. As an addendum, there is a BSD equivalent ( |
re: benchmarks, here's a simple one: On a digital ocean single-core VM running Ubuntu 14.04LTS, I tested splicing two TCP connections. In short:
Since this is loopback, I'm not sure it's even hitting the NIC, but it's nice to see that the performance is there even without the hardware acceleration. I'll have access to a 16-core linux machine with a phat NIC next week, so I'll get back to you with numbers more representative of "enterprise-grade" hardware. |
splice
for net.Conn.ReadFrom(net.Conn)
on Linuxsplice
for TCPConn.ReadFrom on Linux
We could also do better on file2file copies. relevant for the Linux part: http://yarchive.net/comp/linux/splice.html |
@nightlyone The difference ordinary splicing and socket splicing in Go is that sockets are non-blocking. The socket->socket splice is implemented optimally only if it is inside |
@philhofer you are correct, sorry for the noise! |
Just adding a +1 from us here at Comcast for the go1.6 timeframe. We have transfers that run in the 10+MB range as our bread and butter and playing with this for us is around 2x at those sizes. |
FWIW, we rebuilt one of our proxy server with @philhofer 's patch and deployed it in production a few days ago. CPU and memory usage are down. Bandwidth is up. No issues were encountered so far. Applying patches to the standard library is fairly easy for us as we're doing static builds but it would still be nice to see this feature make into the official tree. |
@philhofer care to bring this up on the ML as @minux suggested? |
@philhofer Can you add some example of source code? I have some trouble with understanding it. Is all what need to call io.Copy(dst, src) ? It wrap splice system call only for socket to socket copy operation? |
Hi @philhofer. Thank you for your patch. I have make a simple TCP/IP proxy, it transfer small messages between client and backend by After I apply your patch, the proxy always have 200ms latency. At last, I found that the When I remove the flag, the latency gone and the performance better then old This is the simple proxy: package main
import (
"io"
"net"
)
type Proxy struct{}
func newProxy() Proxy {
return Proxy{}
}
func (p Proxy) Transfer(conn1, conn2 net.Conn) error {
errChan := make(chan error, 1)
go func() {
_, err := io.Copy(conn2, conn1)
conn1.Close()
conn2.Close()
errChan <- err
}()
_, err1 := io.Copy(conn1, conn2)
conn1.Close()
conn2.Close()
err2 := <-errChan
if err1 != nil {
return err1
}
return err2
} The test case: package main
import (
"bytes"
"io"
"math/rand"
"net"
"testing"
)
func Test_Proxy(t *testing.T) {
// Setup an echo server
backendLsn, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
t.Fatal(err)
}
go func() {
conn, err := backendLsn.Accept()
if err != nil {
panic(err)
}
io.Copy(conn, conn)
}()
// Setup a proxy server
proxyLsn, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
t.Fatal(err)
}
go func() {
conn, err := proxyLsn.Accept()
if err != nil {
panic(err)
}
// Dial to backend echo server
agent, err := net.Dial("tcp", backendLsn.Addr().String())
if err != nil {
panic(err)
}
// Begin transfer
proxy := newProxy()
proxy.Transfer(conn, agent)
}()
conn, err := net.Dial("tcp", proxyLsn.Addr().String())
if err != nil {
t.Fatal(err)
}
for i := 0; i < 100000; i++ {
b1 := RandBytes(256)
b2 := make([]byte, len(b1))
_, err := conn.Write(b1)
if err != nil {
t.Fatal(err)
}
_, err = io.ReadFull(conn, b2)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b1, b2) {
t.Fatal()
}
}
}
func RandBytes(n int) []byte {
n = rand.Intn(n) + 1
b := make([]byte, n)
for i := 0; i < n; i++ {
b[i] = byte(rand.Intn(255))
}
return b
} |
Can you tell me how will you use syscall.Splice in proxy code to forward the request directly to Server by doing kernel level copying using Splice instead of io.Copy ? Splice function is look like this : From the net.Conn or http.Request how will we get the value of readfd and writefd ? |
@SubrataPucsd you can see how go does I imagine it would be similar with this (attempt a type assertion to the required underlying type, bail out if it fails) Any progress on this? The benefits seem to have been made apparent. At the very least this would reduce memory allocation. |
splice
for TCPConn.ReadFrom on Linuxsplice
for TCPConn.ReadFrom on Linux
If you know how to make splice get used automatically, like we do for sendfile already, without introducing any new API, great. I don't see how immediately but feel free to send a CL. |
splice
for TCPConn.ReadFrom on Linux
Is zero copy now part of golang 1.9? |
@flavioaiello We don't use the issue tracker for general questions. Please ask on a forum; see https://golang.org/wiki/Questions. If you are asking specifically whether we use the |
Change https://golang.org/cl/107715 mentions this issue: |
sendfile
only allows the source to be ammap
-able file descriptor. Usingsplice
in the implementation of(net.Conn).ReadFrom
allowsnet.Conn
to get the same sort of performance benefits as*os.File
whenio.Copy
is used. (In theory, the src can be any fd.)Pros:
io.Copy
.Cons:
splice
, and a call topipe
, along with associatedpd.WaitRead()
/pd.WaitWrite()
business.For people writing proxies in Go, this kind of optimization could be huge.
If this sounds agreeable, I can send in a patch next week.
The text was updated successfully, but these errors were encountered: