Asynchronous & High-Performance I/O

the C10k (and C10M) problem

/ C10k -> see-ten-KAY; C10M -> see-ten-EM /

The C10k problem is a famous challenge first framed around 1999: how can a single server handle ten thousand simultaneous network connections? The 'C' is for concurrent connections and '10k' for ten thousand. At the time the obvious approach - one thread or process per connection - fell apart at that scale, and the question forced a rethink of how servers are built. C10M is the modern, much harder restatement: ten MILLION concurrent connections.

Why one-thread-per-connection breaks down: each thread needs its own stack (commonly around a megabyte of address space), the kernel scheduler must track and context-switch among thousands of them, and most are just idle waiting for I/O - so you pay enormous memory and switching cost for threads doing nothing. The C10k answer was to invert the design: use non-blocking descriptors plus a scalable readiness mechanism (epoll, kqueue) and an event loop, so ONE thread, or a small pool matching the CPU cores, drives ten thousand connections, spending CPU only on the handful that are actually active at any instant. C10M raises the bar so far that even the kernel's per-packet and per-syscall overhead becomes the bottleneck, pushing techniques like SO_REUSEPORT sharding, careful NUMA placement, zero-copy paths, and even kernel-bypass user-space networking (DPDK).

Why it matters: C10k is the reason the entire async-I/O toolkit exists - epoll, kqueue, IOCP, io_uring, event loops, and modern async runtimes were all shaped by it. Understanding it explains WHY high-performance servers avoid blocking calls and thread-per-connection designs. The honest nuance: ten thousand idle connections and ten thousand busy ones are very different problems; C10k is mostly about cheaply holding many MOSTLY-idle connections, while serving ten thousand truly active ones is bounded by raw CPU, memory bandwidth, and the network.

/* thread-per-conn: 10000 conns ~ 10000 stacks ~ 10 GiB of stack address space + 10000-way scheduling */ /* event loop: 10000 conns ~ 1 thread + 1 epoll set, CPU spent only on the active few */

The same ten thousand connections cost wildly different resources depending on the design.

C10k is a scaling/architecture problem, not a single API. Modern OSes can hit C10k with epoll/kqueue almost trivially; the harder, ongoing frontier is C10M, where per-packet kernel overhead dominates and people turn to sharding, zero-copy, and kernel-bypass. Holding idle connections is cheap; doing real work on all of them is not.

Also called
ten thousand concurrent connectionsC10k 問題(一萬條並行連線)