← Concepts

阅读中文版本

Zero-Copy Transfer

Zero-copy transfer moves data between file descriptors without copying through user space, so a large file send costs no CPU copy and no user-space buffer allocation.

performance

Definition

Zero-copy transfer is the optimization where data is moved between file descriptors — file to socket, file to file — using kernel facilities (sendfile, splice) that transfer directly within the kernel, so that the data is never copied to user space, eliminating the CPU copy and the user-space buffer allocation for large transfers.

Context and scope

Applied in static file serving, proxy data forwarding, and any large transfer where the copy through user space is a measurable CPU and memory cost.

Common misconceptions

  • Zero-copy is always faster — it is faster for large transfers; for small transfers the syscall setup cost can exceed the copy it avoids
  • sendfile works for any file — it works for regular files to sockets; for other combinations (pipe to socket, file to pipe) splice or copy_file_range is needed

Anti-patterns

  • Using zero-copy for tiny transfers where the syscall overhead exceeds the copy saved
  • Calling sendfile without a fallback for the case where the kernel does not support it, failing on older systems

Related concepts

Authoritative source

https://man7.org/linux/man-pages/man2/sendfile.2.html

What it is

Zero-copy transfer is the optimization where data is moved between file descriptors — file to socket, file to file — using kernel facilities (sendfile, splice) that transfer directly within the kernel, so that the data is never copied to user space, eliminating the CPU copy and the user-space buffer allocation for large transfers. Applied in static file serving, proxy data forwarding, and any large transfer where the copy through user space is a measurable CPU and memory cost. Performance is a distribution, not a number: the tail determines user experience, and the mean hides the outliers that cause abandonment.

How it works

Measurement targets percentiles at the workload’s actual concurrency, not a single sequential run. The fast path is kept fast by deferring work that is not on it — lazy initialization, coalescing writes, avoiding allocation in hot loops. Profiling finds the bottleneck; micro-optimization without a profile optimizes the wrong thing. Constant factors matter, but algorithmic complexity dominates as scale grows.

When to optimize and pitfalls

Optimize when a measurement shows a concrete cost — latency budget burn, compute spend, user-visible jank. The pitfall is optimizing for the benchmark rather than the workload: a synthetic test that does not reproduce the production access pattern produces a local maximum that hurts globally. Another is trading memory for speed without accounting for the GC or eviction cost the extra allocation introduces.