NUMA 的平衡和调度

前面介绍 Linux 的物理内存管理时,曾提及过 NUMA。CPU 对内存的访问受制于**「内存带宽」**的影响,NUMA 系统将 CPU 和内存划分到不同的节点(node) 上,可以让内存的访问更好地利用 memory bandwidth(本质是硬件层面的并行性)。

但是随之而来的弊端是:一个 CPU 所访问的内存不一定和它在同一个 node 上。如果恰好在同一 node(local access),那么访问时延较短:

而如果不在同一 node(remote access),就需要途经 node 之间的 interconnect(比如 Intel 的 QPI links),相比 local access 就会造成更大的 latency:

内存的分配决定了后续的内存访问方式,因此在 NUMA 系统里,需要首先考虑内存从哪里分配比较合适。

内存分配策略

从本 node 上分配的好处是显而易见的,但是如果出现本 node 上内存已经不够,而其他 node 上还有富余内存的情况,是选择在本 node 上启动内存回收,还是适当地从其他 node 分配呢?

对于 per-cpu 的内存,往往只能坚持 node local 的方式,其他情况则可以考虑就近的 node。所谓“就近”,指的是 numa distance(可使用 “numactl --hardware” 命令查看):

一般小型 NUMA 系统(node 数目为 2 的)倾向于从其他 node 分配,而对于大型 NUMA 系统(node 数目大于等于 4 的),由于跨 node 的内存访问的代价更大,则往往宁愿 reclaim。

具体的决策由内核参数 “/proc/sys/vm/zone_reclaim” 控制,其值默认为 0,表示从其他 node/zone 分配。如果该值设为 1,虽然会回收,也尽量选用轻量级的方式,即只回收 unmmaped 的 page cache 页面【注-1】。如果为 2,则可以回收造成开销更大的,需要 writeback 的页面。为 4 的话,那么需要 swap 操作的匿名页面也会被包含。

而在系统 boot-up 的时候,为了减少在同一 node 上的内存访问拥塞,选择了以 round-robin/interleave 的形式在各个 node 上分配内存,等到 init 进程启动后,再切换到默认的 local 模式 。

这在 NUMA 里叫做 memory policy,由于是和内存分配相关,更准确地应该叫做 allocating policy。策略可以针对整个系统进行设置,也可以按 task 级别设置,比如:

numactl --membind=2 dd if=/dev/zero of=/dev/shm/A bs=1M count=1024

此处之所以用 “task” 这个词,是因为它既可以是 process,也可以是 thread。可是一段内存是可能被多个进程共享的(比如动态链接库,或者由同一 elf 启动的进程的 text segment),同一个进程的多个线程之间也可能共享 VMA,那么如果不同的 task 对同一个内存设置了不同的 policy,该如何是好?

这里又有一个 first touch 的原则,即由第一次访问这段内存的 task 的 policy 决定。

内存迁移的影响

内存分配造成的 remote access 只是一方面,前面讲负载均衡的文章说过,到了 NUMA 域这一层,任务可能在不同的 node 之间迁移,这也会导致 cross-node 的内存访问。

load balancer 的脑子里只会想到 CPU 之间的负载差异,它可考虑不到这些,所以我们需要另外一些机制,来减少任务迁移带来的损失。

一种是 Memory follows CPU,即任务迁移后,把它在原先 node 使用的内存也迁移(拷贝)过来,之后对这部分内存的访问就变为 local access 的模式了。

另一种是 CPU follows Memory,即通过定期的扫描,统计一段时间内一个任务对各个 node 上的内存的访问数量,然后将任务置放到其访问最多的那个 node 上去运行。

由于底层硬件架构和应用层 workload 的多样性,实际需要考虑和权衡的因素还有很多,而这一切,都是由一套被称为 automatic NUMA balancing 的框架来完成的(可通过_“/proc/sys/kernel/numa_balancing”_ 来打开或者关闭)。

AutoNUMA 的主要依据是尽量减少 remote access,但随着硬件技术的进步,跨 node 的内存访问延迟已经有了很大的改善,根据这篇论文的研究,remote access 相比 local access 的差异已经从早期的慢 7 倍提升到只慢 30%。

这意味着 remote access 造成的影响在减弱,那么相对地,其他因素的影响就在上升,当然这不是说减少 remote access 就不再重要,而是说它不再是唯一的目标。

小结

操作系统作为硬件的管理者,其所使用的软件算法会不可避免地随着硬件的发展而调整。而 NUMA 系统的设计,让 CPU 和内存这两种最核心的硬件资源有了结构和层次上的区别,这使得 OS 对任务的调度,既要考虑 CPU 之间负载的平衡,又要兼顾对不同 node 的内存访问的影响,就好比心肺联合移植的外科手术一样,注定了它的难度和复杂性。

**注-1:**unmapped 的页面是指没有被进程页表映射的 page frame,这样的页面在 reclaim 时不需要通过 reserve mapping 解除映射关系,回收最为迅速,所以保持一定比例的 unmapped 页面是有必要的,这又引出另一个内核配置参数,即 “/proc/sys/vm/min_unmapped_ratio”(默认为 1%)。

参考:

  • 内核文档 Documentation/admin-guide/sysctl/vm.rst
  • 内核文档 Documentation/admin-guide/mm/numa_memory_policy.rst
<think>好的,我需要帮用户找到关于NUMA架构下CPU绑定的配置方法。首先,我得回忆一下NUMA的基本概念,确保自己理解正确。NUMA是Non-Uniform Memory Access的缩写,主要用于多处理器系统中,每个CPU节点有本地内存,访问本地内存比访问其他节点的内存更快。这有助于解决SMP架构扩展性问题。用户提到的引用[1][2]也提到了这点,特别是引用[2]详细说明了NUMA的结构,包括CPU模块互联模块。 接下来,用户需要具体的配置方法。根据引用[3],提到了通过修改smp_affinity来绑定中断到特定CPU。例如,echo 2 > /proc/irq/74/smp_affinity是将中断绑定到CPU1。这可能是一种方法,但用户可能还需要更全面的配置,比如进程绑定到CPU节点。 另外,引用[4]提到了numactl工具的使用,这个工具可以用来管理NUMA策略。我需要详细说明如何使用numactl来绑定进程到特定的CPU节点内存节点。例如,使用numactl --cpubind--membind选项,或者组合使用--physcpubind来指定具体的CPU核心。 可能还需要提到taskset工具,它可以通过设置CPU亲性来绑定进程到特定的CPU核心,例如taskset -c 0,1 command。不过,taskset不处理内存的NUMA策略,所以结合numactl会更全面。 然后,我应该分步骤说明配置方法,比如使用numactl进行绑定,修改smp_affinity处理中断,以及通过Kubernetes配置(如果适用)。同时,要指出注意事项,比如绑定后的性能测试避免过度绑定。 最后,生成相关问题,帮助用户扩展了解,比如性能优化方法、监控工具、容器中的配置差异等。确保引用正确,如引用[3][4]中的具体方法。 需要检查LaTeX格式是否正确,比如数学表达式是否用$包围,独立公式用$$,但这里可能用不上太多数学公式,所以主要确保代码块命令行示例正确。另外,使用中文回答,结构清晰,分步骤说明。</think>### NUMA架构下CPU绑定的配置方法教程 在NUMA架构中,通过CPU绑定(也称为CPU亲性配置)可以优化进程的调度内存访问效率,减少跨节点访问的延迟。以下是具体配置方法: --- #### **1. 使用`numactl`工具进行绑定** `numactl`是NUMA架构中常用的配置工具,支持将进程绑定到特定CPU节点内存节点。 - **绑定CPU内存节点**: ```bash numactl --cpubind=<节点编号> --membind=<节点编号> <命令> # 示例:将进程绑定到NUMA节点0的CPU内存 numactl --cpubind=0 --membind=0 python app.py ``` - **仅绑定CPU节点**: ```bash numactl --cpubind=<节点编号> <命令> ``` - **绑定到具体CPU核心**: ```bash numactl --physcpubind=<CPU核心列表> <命令> # 示例:绑定到CPU0CPU1 numactl --physcpubind=0,1 ./benchmark ``` --- #### **2. 通过`taskset`设置CPU亲性** `taskset`可直接绑定进程到特定CPU核心,但**不处理内存的NUMA策略**: ```bash taskset -c <CPU核心列表> <命令> # 示例:绑定到CPU0CPU2 taskset -c 0,2 java -jar service.jar ``` --- #### **3. 中断绑定(IRQ Affinity)** 对于硬件中断,可通过修改`smp_affinity`文件绑定到特定CPU核心[^3]: ```bash # 查看中断编号 cat /proc/interrupts # 绑定中断74到CPU1(0x2对应二进制0b10) echo 2 > /proc/irq/74/smp_affinity ``` --- #### **4. Kubernetes中的NUMA配置** 在Kubernetes中,可通过资源限制策略实现NUMA感知调度[^4]: ```yaml apiVersion: v1 kind: Pod metadata: name: numa-app spec: containers: - name: app image: nginx resources: limits: cpu: "2" memory: "4Gi" requests: cpu: "2" memory: "4Gi" # 指定NUMA策略 command: ["numactl", "--cpubind=0", "--membind=0", "nginx"] ``` --- #### **注意事项** 1. **性能测试**:绑定后需通过`numastat`或`perf`工具验证跨节点访问次数(如`numa_miss`指标)。 2. **避免过度绑定**:若节点负载不均衡,可能导致资源浪费。 3. **兼容性**:部分老旧应用可能不支持NUMA策略,需测试验证。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值