-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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: interleaving sendfile and read calls are very slow #45256
Comments
The C code that I can see doesn't seem to be comparable, because it using blocking I/O, but Go always nonblocking I/O. What I see from If I change the file size to 65536, every loop iteration takes roughly the same amount of time. I don't know why the kernel is only returning 65536 bytes for the first |
@ianlancetaylor indeed it seems like the client is the issue look at strace. But it's still weird that changing the server to not use sendfile or the blocking IO C implementation makes it fast. It seems to be some timing issue. When I put a But the timing issue always seems present when using The output I get from this program is:
But when I remove
As you can see even with the Again wrapping the I'm wondering if this is a deeper issue we can fix or if we should at least make it so |
As I test, the C code also behaves similarly to go, If the count param of |
if the count param of sendfile larger than the file size to be sent, the TCP will cause a 200ms delay somehow. Fixes golang#41513 Fixes golang#45256
Change https://golang.org/cl/305229 mentions this issue: |
This indeed seems to fix the problem. You can test this by passing an |
Running a server and a client on the same system is not the common case, and is not the case for which we should optimize. Can you do some measurements where the two programs are on different systems? Thanks. |
I have done some tests between servers with <1ms latency and with ~6ms latency and in both cases it's not noticeable. So I'm guessing its indeed some weird behavior with nonblocking I/O and sendfile where all streams are on the same machine. |
It's semi common when you run a https server that proxies to multiple microservices on the same machine. |
I write a non-blocking IO framework, and tried the sendfile test, it seems to work fine on the non-blocking fd: Output: go run bench.go
277.042µs
237.266µs
288.234µs
219.801µs
238.872µs
260.966µs
274.589µs
265.921µs
205.281µs
250.087µs erikdubbelboer's std version: go run ./std.go
486.578µs
209.483756ms
207.547278ms
208.009642ms
43.189515ms
208.18629ms
208.294767ms
43.925097ms
211.092839ms
208.350165ms both were run on my laptop, same ubuntu VM. |
https://github.com/lesismal/nbio/blob/517442a262d0e26fecf1a380cddd403ae70bdb14/sendfile_linux.go#L32, your sendfile use the size of the file, this is the thing. |
You are right, I tried a larger size, then get a slower result, same as the io.Copy and c code. I tried another test: set the read buffer size for the new connection to be large enough before the data interaction started. The new test performs better than the original one: Output: default read buffer:
309.454µs
209.564193ms
207.495256ms
206.871764ms
46.101953ms
205.103742ms
210.380848ms
44.039169ms
210.697342ms
209.346464ms
read buffer 1024*1000:
218.201µs
142.148µs
138.761µs
102.964µs
86.283µs
72.657µs
72.307µs
120.246µs
257.105µs
153.59µs The kernel implementation of sendfile relies on splice, I read the kernel code but not familiar and not sure about it: |
it seems better to use the size of the file: as https://github.com/golang/go/blob/master/src/net/sendfile_unix_alt.go#L43 did |
Rediscovered as #61530 |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
Calling
io.Copy
triggeringsendfile
multiple times on a socket with the same data size.What did you expect to see?
Each
io.Copy
call to take the same amount of time.What did you see instead?
The first
io.Copy
call is fast at around500µs
. All following calls toio.Copy
are slow at around43ms
.I have written a simple program to demonstrate the issue: https://gist.github.com/erikdubbelboer/3eebecc551245a3b1d5f06a9ef433654#file-bench-go
I have also included a C version of the server to show that implementing similar code in C doesn't have the issue: https://gist.github.com/erikdubbelboer/3eebecc551245a3b1d5f06a9ef433654#file-server-c
Output:
Interesting is also that removing the 1 byte that is being send to the server first, and just having the server send all 10 files at once doesn't trigger the performance impact. Also not when replacing the reading of the 1 byte with a
time.Sleep(time.Millisecond)
. So it seems that something about the data coming from the other direction causes the followup sendfile calls to be slow.The reason why I'm thinking this is related to
sendfile
is because when I useio.Copy(writerOnly{conn}, fd)
withwriterOnly
fromnet/net.go
it doesn't affect performance.writerOnly
as documented in the Go source doesn't implementio.ReaderFrom
(likenet.TCPConn
does) and doesn't trigger sendfile.Not using
sendfile
at all is also fast:This mostly impacts
net/http
file serving on keep-alive connections. This is why I included the 1 byte being send from the client to the server first as it imitates http behavior.The text was updated successfully, but these errors were encountered: