跳转至

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 路径短时间忙等网卡数据,适合极低延迟场景,但需要结合内核版本、驱动和系统参数验证。

常见陷阱

  1. 只绑业务线程,不绑 NIC IRQ,数据仍跨 CPU 跳转。
  2. 线程绑到了同一个物理核心的 SMT sibling,互相干扰。
  3. 忽略 NUMA,线程和内存在不同 socket。
  4. busy polling 用在普通服务上,浪费 CPU 且收益很小。

观测与调试

cat /proc/interrupts
cat /proc/<pid>/status | grep Cpus_allowed_list
perf stat -e context-switches,cpu-migrations ./app