CPU 亲和性与 busy polling
是什么 / 解决什么问题
低延迟服务要减少线程迁移和调度唤醒。CPU affinity 把线程固定在指定 CPU;busy polling 用自旋等待事件,避免睡眠和唤醒成本。
使用模式
# 把进程绑定到 CPU 2
taskset -c 2 ./app
# 查看线程在哪些 CPU 上运行
ps -L -o pid,tid,psr,comm -p <pid>
C 代码里可以用 sched_setaffinity:
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(2, &set);
sched_setaffinity(0, sizeof(set), &set);
busy polling
阻塞等待:
read(fd, buf, sizeof(buf)); // 没数据时睡眠,依赖调度唤醒
自旋等待:
while (!has_data()) {
__builtin_ia32_pause();
}
自旋能减少唤醒延迟,但会持续占用 CPU。交易系统通常把关键线程独占核心。
Linux 网络相关选项
int val = 50; // microseconds
setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL, &val, sizeof(val));
SO_BUSY_POLL 让 socket read/poll 路径短时间忙等网卡数据,适合极低延迟场景,但需要结合内核版本、驱动和系统参数验证。
常见陷阱
- 只绑业务线程,不绑 NIC IRQ,数据仍跨 CPU 跳转。
- 线程绑到了同一个物理核心的 SMT sibling,互相干扰。
- 忽略 NUMA,线程和内存在不同 socket。
- busy polling 用在普通服务上,浪费 CPU 且收益很小。
观测与调试
cat /proc/interrupts
cat /proc/<pid>/status | grep Cpus_allowed_list
perf stat -e context-switches,cpu-migrations ./app