A small file-serving service of mine slowed to a crawl one afternoon after a “harmless” middleware change. CPU on the server box doubled, throughput roughly halved. The diff was a single line: instead of handing a *os.File to io.Copy , somebody had wrapped it in a tiny logging reader to count bytes.
That one wrap quietly turned off sendfile(2) .
This post is about that fast path: what Go does for you for free, how to see it actually fire, and the surprisingly easy ways to lose it.
The setup
Linux 6.6 / Ubuntu 24.04 (WSL2), AMD Ryzen 5 9600X, 16 GiB RAM
Go 1.22.12
512 MiB random-bytes file, page cache warm
Every benchmark below serves the same big.bin file over plain TCP to a Go client on the same machine. Server pinned to CPU 0, client to CPU 1, so we can read /usr/bin/time server-side and compare apples to apples. Syscall counts come from a vanilla strace -c -e trace=read,write,sendfile,splice .
What sendfile actually does
A normal “send this file” looks like this:
... continue reading