Skip to content

PAC-26 Summary:NUMA结构下的内存访问问题

1. 引言:「PAC-26」张量缩并赛题的数据分发问题

今年参加的PAC26初赛早在一周前就已结束,本来应该结束后就写下总结的,但无奈和PRA决赛时间重合,因此等PRA结束后才动笔写下总结。

初赛我负责的是Task1——张量缩并 kernel 优化。由于初赛时间段和MCC-Final重合,因此做PAC-Pre的时间较短。再加上受一些事情的干扰,内心比较浮躁,没能正确分析问题,导致后期一直卡在memory bound,致使初赛成绩不甚理想,进入决赛机会渺茫。不过这也正好,打比赛的这一个月过得太浮躁了,也时候静下心来进行沉淀,梳理一下阶段性的收获。这沉淀的第一步,就从PAC-Pre意识到的对数据访问机制的认知缺陷入手,进行学习补充和经验总结。

1.1 赛题内容与瓶颈定位

Task 1 需要优化的 kernel 是张量缩并,由于张量维度已知【分别是(PQRxPSR --> QS)、(TPRSxPQR --> QTS)】,因此我们可以把计算转换成常见的GEMM计算,或者参考GEMM的优化方案,利用已有的 mma 优化进行优化。计算部分优化思路很明确,真正的问题是处理初始数据的传输。

image.png

初始程序按照 Q 维度进行划分,每个 rank 保留划分后的 local_A 数据,只有 rank0 具备完整的 B 数据。因此程序必须先把 rank0 上完整的 B 分发给各个 rank 才能进行下一步的计算。而 B 的分发,正是这个比赛最大的瓶颈。

于是任务便转化成了:在多 MPI rank、多 OpenMP 线程、NUMA + HBM 机器上执行任务时,如何让数据以正确的方式到达计算节点,获得最好的性能

1.2 赛题硬件环境与实际资源绑定

LX2 机器有两个 socket、608 个物理核心;Task 1 只使用 socket 0。该 socket 中的 CPU NUMA 0-7 各运行一个 MPI rank,每个 rank 使用 36 个 OpenMP 线程,共 8 rank、288 个工作核心(每个 CPU NUMA node 保留一个系统隔离核心,因此单socket最大核心使用数量为288)。CPU NUMA 0-15 带 DDR 和核心;HBM NUMA 16-31 为无核心的片上内存节点,每个 HBM 约 4 GiB。

LX2-cpu_topo.png

CPU topo

换言之,这不是一个跨节点、也不是一个跨 socket 的通信问题,而是多核NUMA系统下,sokect内单节点关键计算资源的派发问题

1.3 初始 B 的分发量与实测瓶颈

在进入张量缩并计算之前,Rank 0 所持有的初始 B 矩阵必须分发给其余 7 个进程(Follower)。这一数据分发过程已成为一个独立的性能瓶颈,其访存与通信强度极高(memory bound),因此需要专门进行调度优化,以隐藏其通信延迟。

由于NUMA结构的特点,核心访问远端数据的延迟一般是本地数据的几倍。让agent自动测试的对比程序也体现了这个问题:

项目(张量 B 访问耗时统计)Case 1Case 2
单份 B 的元素布局4 x 10000 x 5000 FP644 x 4 x 5000 x 4000 FP64
单份 B 容量1.60 GB2.56 GB
向 7 个 follower 分发的逻辑数据量11.20 GB17.92 GB
公共 HBM 被 8 rank 直接读取的耗时约 4.805 s约 2.060 s
相同条件下本地读取对照约 0.201 s约 0.144 s
退化倍率23.87x14.35x

让 8 个 rank 在计算中直接重复消费一份公共 packed B(也就是7个rank的数据都需要进行访问远端内存),Case 1 数据搬运部分从约 0.201 s 恶化到 4.805 s,Case 2 从约 0.144 s 恶化到 2.060 s。这些数字排除了“共享一份 B 就能省掉复制”的假设:多 rank 同时跨 NUMA 读取同一份数据相对于访问本地内内存的数据,不仅访问延迟更高,还会争抢源 HBM 的 IMC 和片上互连带宽,造成更长时间的等待。因此后续优化都建立在数据搬运到本地内存进行访问的基础上。

因此,任务的中心转化为在多 MPI rank、多 OpenMP 线程、NUMA + HBM 机器上执行任务时,如何让单源数据尽快转发到其他 NUMA node 中

1.4 从 MPI 广播到细粒度双缓冲:最终方案仍然受限

定位到 B 的分发瓶颈后,我先后尝试了 MPI 广播算法调参、 B 共享、分块 MPI_Ibcast、两组 HBM/树状 relay,以及将 tile 复制到各 rank 私有 HBM 等方案。前几种方案分别受限于广播等待、共享内存申请与释放、远端 HBM 争用或额外的跨 NUMA 流量,均未解决根本问题。

最终采用的是 shmem + CPU memcpy + 细粒度 tile + double buffering:rank 0 把 B 分块发布到共享内存,其他 rank 用复制线程将 tile 搬到本地 HBM;复制下一块与计算当前块通过双缓冲重叠。这避免了计算阶段持续访问公共远端 B,但仍存在三个缺陷:

  • CPU memcpy 的数据路径为 源内存 -> 源 IMC -> Fabric -> CPU cache line/load-store -> 目标 cache line/store buffer -> Fabric -> 目标 IMC -> 目标内存。数据必须经过 CPU 和缓存,不仅增加搬运开销,还会占用计算核心、load/store 通路和缓存资源;

  • 7 个 follower 仍从同一共享源读取,源 HBM 与 Fabric 依然是热点;

  • 细粒度 tile 增加了实际传输和同步次数,使上述 CPU/缓存路径的开销被反复累积;双缓冲也容易因单个慢 rank 产生流水线阻塞。

这也是我初赛所负责的张量缩并赛题性能孱弱的原因所在。

下来进行优化方案梳理并学习其他队伍的PPT后,我发现更合适的方案应是 MPI + 大块数据传输 + SDMA + 多级 buffering:MPI 负责拓扑和同步,SDMA 将连续大块直接搬入各 rank 的本地 HBM,多级缓冲用于隔离源端、转发层和计算端的速度差异。这样既能减少细粒度同步,也能避免 CPU 参与 payload 复制,为计算保留更多核心和缓存资源。

时间不能倒流,我也无法再去修改我的代码,虽然最后的结果满是遗憾,但这也说明我还有很大的成长空间。做这个比赛我最直观的感受是对数据搬运方式和链路相关信息的缺失:不知道数据怎么从内存经过怎样的链路到达指定的目的地;也不知道除了MPI、memcpy之外还有哪些方式可以搬运数据,还有哪些硬件可以加速这个过程。因此,提交代码后我开启了对数据搬运相关内容的学习。


2. 为什么现代多核 CPU 需要 NUMA

随着核心数量、内存容量和带宽需求不断增加,如果所有核心都经过同一个内存控制器和共享总线访问内存,控制器和总线会迅速成为瓶颈。NUMA 将内存控制器和内存资源分布到多个局部域中,使各组核心能够并行使用邻近内存,从而扩展整机的容量与聚合带宽。它的代价是不同核心访问同一地址空间的成本不再相同,程序必须主动考虑数据放置和访问路径。

image.png

2.1 NUMA 的结构特点与基本查看方法

一个 NUMA 系统通常由多个局部域组成。每个域包含一组 CPU 核,并与某个内存控制器及 DDR/HBM 内存节点保持较近的物理关系。核心访问本地节点时路径更短、争用更少;访问其他节点时,请求需要经过片上互连或 socket 间链路,因此延迟更高、可用带宽也更容易受到并发流量影响。虽然访问成本不同,各 NUMA 域仍属于统一的物理地址空间,可以在缓存一致性协议和互连网络的支持下相互访问数据。

image.png

常用的拓扑查看命令包括:

Plain
lscpu -e=CPU,CORE,SOCKET,NODE,ONLINE
numactl --hardware
cat /sys/devices/system/node/node*/distance
lstopo --whole-io --of console
lstopo --of svg topo.svg

lscpu 给出 core、socket 与 NUMA node 的映射;

numactl --hardware 给出节点的 CPU、容量和距离矩阵;

lstopo 适合观察 CPU、NUMA 与 PCIe 设备之间的层级关系。注意,NUMA distance 只表示相对访问代价,lstopo 的图形布局也不等于芯片内部真实的 Fabric 走线。

2.2 数据访问相关术语

以下是在数据访问中常见的术语解释

术语含义
NUMA node一组具有共同局部性的 CPU 或内存资源;内存节点也可能没有 CPU 核
Local / Remote memory相对于当前核心而言的本地/远端内存;区别在访问路径和成本,不在地址是否可见
Cache lineCPU 缓存和一致性协议管理数据的基本单位,通常一次传输一整条 cache line
Cache coherence保证多个核心看到同一地址的最新有效数据,必要时可从另一核心的缓存取得脏数据
IMCIntegrated Memory Controller,集成内存控制器,负责把片上请求调度到 DDR/HBM
Fabric / Mesh连接 core、cache、IMC 和 I/O 设备的片上互连;Mesh 是其中一种网格状拓扑
DMA / SDMA不依赖 CPU 逐条执行 load/store 的数据搬运引擎;SDMA 是平台提供的一类专用 DMA 能力
Shared memory / shmem让多个进程映射同一组物理页的机制,本身不负责生成各 NUMA 节点的本地副本
MPI定义 rank 间消息、集合通信和同步的软件接口;底层可能选择共享内存、DMA 或网络路径
NIC / HCA节点间通信的网络控制器,可通过 DMA/RDMA 读写主机注册内存
Payload真正需要搬运的数据,例如 B 矩阵;描述符、状态和通知不属于 payload
DoorbellCPU 通过 MMIO 写入设备的工作通知,表示队列中已有新的搬运任务

2.3 NUMA 系统中的主要数据访问方式

按照硬件的组织方式,在NUMA系统中,数据的访问可以分为NUMA node内访问、跨NUMA node访问、跨Socket访问以及跨节点通信,以下是常见的几种访问方式。

访问方式常见软件调用主要硬件典型数据链路
CPU 本地/远端读取指针访问、load/storecore、cache、一致性单元、IMCmemory -> IMC -> Fabric/cache -> core
共享内存直接访问mmapshm_open、System V shmem、MPI_Win_allocate_shared与普通 CPU load/store 相同shared page -> IMC -> Fabric -> requesting core
CPU 内存复制memcpy、并行 copy loopCPU load/store、cache、store buffer、IMCsource memory -> CPU -> destination memory
本机设备复制KUPL/厂商 DMA APISDMA、IMC、Fabricsource memory -> SDMA -> destination memory
节点内 MPIMPI_BcastMPI_IbcastMPI_Send/Recv取决于 MPI 实现,可为 CPU shmem 或 SDMA路径由所选 transport 决定
跨节点通信MPI、RDMA verbsNIC/HCA、PCIe、网络 Fabriclocal memory -> NIC -> network -> remote NIC -> remote memory

3. NUMA 下的数据访问路径

3.1 从一次 load 到完整的数据搬运

以 PAC 中 rank 0 向其余 rank 分发本地关键计算资源 B 为引,数据有以下几种访问情况。

3.1.1 直接读取远端 B

rank 1 发出 load 后先查询 TLB 和本地 L1/L2。cache miss 时,请求经 Fabric 到达 B 所在节点的 IMC;内存返回一条 cache line,再经 Fabric 进入 rank 1 核心的缓存和寄存器。如果最新数据仍是另一核心缓存中的脏副本,一致性协议会先从该缓存取得数据。该方式没有显式复制,但 7 个 follower 重复读取同一远端 B 时,源 IMC 和共享 Fabric 会成为热点。

Plain
remote memory -> remote IMC -> Fabric -> local L2/L1 -> register -> compute

3.1.2 使用 CPU memcpy 生成本地副本

CPU 先沿上述路径读取源数据,再执行向量 load/store,把数据写入目标 NUMA 节点。普通 cached store 还可能触发目标 cache line 的 RFO/write-allocate,脏数据随后经目标 IMC 写回内存。因此 memcpy 不是 IMC 到 IMC 的直连搬运,而是 CPU 参与的读写过程。

Plain
source memory -> source IMC -> Fabric -> CPU cache/register
-> store buffer/cache -> Fabric -> target IMC -> target memory

只从指令吞吐估计,单核心复制 payload 的理论上限为:

Plain
BW_copy_core = min(L x V, S x V) x f

其中 V 是向量字节数,L/S 是每周期 load/store 指令吞吐,f 是频率。实际大块复制还受源 IMC、Fabric、目标 IMC 和多核争用限制,不能把单核结果直接乘以核心数。non-temporal store 可以减少 RFO 和缓存污染,但是否有利取决于复制后是否立即消费数据。

3.1.3 使用 SDMA 生成本地副本

应用先注册源、目标缓冲区并提交描述符,SDMA 随后作为 bus master 发起读写事务;CPU 只负责提交和完成通知,payload 通常不进入 CPU L1/L2,不会造成缓存污染。

Plain
source memory -> source IMC -> Fabric -> SDMA queue
-> Fabric -> target IMC -> target memory -> completion

3.1.4 跨 socket 直接访问远端内存

发起 load 的核心先查本地 TLB/L1/L2;发生 miss 后,请求从发起 socket 的片上 Fabric 进入 socket 间互连(例如 Intel 的 UPI),再到目标 socket 的 Fabric 和 IMC。数据返回时沿相反方向回到发起核心。如果目标 cache line 的最新副本在远端核心缓存中,一致性请求会先经过 socket 间互连取得该副本,未必访问目标 IMC。

Plain
requesting core
-> source socket Fabric
-> socket interconnect (UPI 等)
-> target socket Fabric
-> target IMC -> target memory
-> target socket Fabric -> socket interconnect
-> source socket Fabric -> requester L2/L1 -> register

这里的“跨 socket”描述的是物理封装边界;操作系统通常把目标内存暴露为另一个 NUMA node。相同 socket 内不同 NUMA node 则只经过本 socket 的 Fabric,不经过 UPI。

3.1.5 跨 socket 的 CPU memcpy

CPU 复制时,源端读取和目标端写入分别形成事务。假设复制核心位于 socket A,源数据和目标数据都位于 socket B,则源读先从 B 经过互连到 A 的核心,目标写又从 A 经过互连回到 B;payload 可能两次穿过 socket 间链路。

Plain
source memory (socket B)
-> B IMC -> B Fabric -> socket link -> A Fabric
-> A core L1/L2/register/store buffer
-> A Fabric -> socket link -> B Fabric
-> B IMC -> target memory (socket B)

若源数据在 A、目标在 B,只需在写入阶段跨 socket;若源和目标都在本地 socket,则退化为前面的本地 memcpy 路径。因此 CPU memcpy 的跨 socket 代价取决于源、目标和复制核心三者的位置,而不是只由“两个 node 不同”决定。

3.1.6 跨 socket 的 SDMA

SDMA 不经过 CPU 的寄存器和 L1/L2,但 DMA 引擎的挂载位置会影响路径。源内存、SDMA 引擎和目标内存不在同一 socket 时,读事务和写事务中相应的一段会经过 socket 间互连:

Plain
source memory -> source IMC -> source Fabric
-> socket interconnect (若 SDMA 在另一 socket)
-> SDMA read queue / in-flight buffer
-> socket interconnect (若 target 在另一 socket)
-> target Fabric -> target IMC -> target memory
-> completion queue / interrupt

这不是两个 IMC 之间的直连线,而是 SDMA 发起的读事务和写事务由 Fabric 连接起来。SDMA 可以减少 CPU load/store、cache 污染和 RFO 开销,但不能绕过源/目标 IMC 或 socket 间链路的物理带宽上限。

3.1.7 跨节点通信(MPI/RDMA)

MPI 是软件通信接口,具体传输可能使用共享内存、TCP 或 RDMA。对大消息常见的 RDMA 数据路径如下:

Plain
sender memory
-> sender IMC -> sender socket Fabric
-> PCIe Root Complex
-> sender NIC/HCA DMA read
-> network Fabric / switch / link
-> receiver NIC/HCA
-> receiver PCIe Root Complex
-> receiver socket Fabric -> receiver IMC
-> receiver registered memory

RDMA 的准备和完成路径由 CPU 参与,但不等于 CPU 搬运 payload:应用先 pin/register 内存,创建通信队列并填写 WQE(工作队列条目),再向 NIC 的 doorbell 寄存器执行一次 MMIO 写入;NIC 据此通过 DMA 读取发送缓冲区、发送网络包,并把数据直接 DMA 写入接收端注册内存。完成后 NIC 写入 CQE(完成队列条目)或触发中断,MPI 线程负责轮询/处理完成事件。

MPI 数据传输涉及到的内容较多,这里不适合展开。

3.2 shmem + memcpyMPI + SDMA

我的PAC 最终方案选择把 B 切成细粒度 tile,再由 follower 使用 CPU memcpy 从共享源复制到本地 HBM。它虽然通过双缓冲隐藏了部分复制时间,但由于重复经过 CPU/cache 路径,并累积了多次发布、等待和槽位复用开销,最终性能依旧不理想。

对比项shmem + memcpyMPI + SDMA
软件职责shmem 提供共享地址,程序自行切块、复制和同步MPI 管理 rank、消息与同步,厂商 transport 调用 SDMA
payload 搬运者CPU coreSDMA engine
数据路径source -> IMC/Fabric -> CPU cache/load-store -> targetsource -> IMC/Fabric -> SDMA -> IMC -> target
CPU 与 cache占用核心、load/store、cache 和 store bufferCPU 主要提交任务,payload 通常绕过 L1/L2
合适粒度小数据或低启动成本复制已注册、连续的大块数据和足够的在途请求
主要风险细粒度同步、缓存污染、源端热点注册/提交成本、队列同步,以及 SDMA 通道和 Fabric 上限

更合理的方向是 MPI + 大块传输 + SDMA + 多级 buffering:将连续大块搬入各 rank 的本地 HBM,用 MPI 管理拓扑和完成关系,用 SDMA 释放 CPU 计算资源,再以源端、转发层和本地计算层的多级缓冲吸收不同路径的速度差异。其他队伍的大块方案实测可将搬运压到 0.1 s 内,是这个赛题最好的数据搬运方式(之一)。需要注意的是 SDMA 并不是天然更快,小块 SDMA 加逐次等待同样会积累提交和同步开销,正确的大块数据 SDMA 搬运才能获得最好的数据搬运效果。


4. 面向程序优化的硬件探测

这次比赛我存在的其中一个问题即是对CPU数据搬运的硬件了解不多,不知道如何查看机器上具有哪些硬件,每种硬件的特性如何,应该如何去使用,自然也就不知道利用sdma去辅助搬运数据。因此,这一部分将说明如何探测陌生机器所拥有的硬件,避免以后再出现类似的问题。

硬件探测应按三个层次进行:先查看静态拓扑,再检查进程运行时的 CPU 与内存位置,最后用性能计数器和基准测试验证带宽。命令只能展示操作系统、固件和驱动公开的信息,不能直接还原芯片内部每一条 Fabric 路由。

4.1 CPU、缓存与 NUMA

命令参数含义主要输出与作用
uname -a-a 表示输出全部基础系统字段内核名称与版本、主机名、机器架构等。用于确认运行环境,但不能据此判断 CPU 的核数、缓存和微架构细节
lscpu无参数时汇总 /proc/cpuinfo 与 sysfs 中的 CPU 信息架构、逻辑 CPU 数、每核线程数、每 socket 核数、socket/NUMA 数量、型号、指令集标志和缓存摘要。可先确认 SVE/SME 等指令集是否被内核公开
lscpu -e=CPU,CORE,SOCKET,NODE,ONLINE-e 输出逐逻辑 CPU 表;等号后指定列CPU 是逻辑 CPU 编号,CORE 是物理核编号,SOCKET 是封装编号,NODE 是 NUMA 节点,ONLINE 表示是否在线。用于生成 rank/thread 的绑核表
lscpu -C-C--caches)切换到缓存表缓存级别、类型、单实例/总容量、组相联度、集合数和 cache line 大小;具体列随 util-linux 版本变化。用于判断哪些核共享某级缓存
numactl --hardware--hardware-H 只显示硬件拓扑,不改变绑定策略可用 NUMA 节点、每个节点的 CPU、内存总量/余量以及 distance 矩阵,是确定 CPU node 与 DDR/HBM node 对应关系的首要命令
cat /sys/devices/system/node/node*/distancenode* 由 shell 展开为每个 NUMA 节点;每个文件是一行距离值i 个文件的第 j 个数表示 node i 到 node j 的相对代价。数值越小通常越近,但它没有时间或带宽单位,也不能单独证明真实链路结构
lstopo --whole-io --of console--whole-io 显示全部 I/O 设备;--of console 输出文本展示 Machine、Package、Group、NUMANode、Cache、Core、PU、PCI Bridge 和 PCI Device 的层级。它适合判断资源共享范围与设备亲近性;Group 不等于 NUMA 节点
lstopo --of svg topo.svg--of svg 选择 SVG 格式;topo.svg 是输出文件生成可缩放的拓扑图,便于查看 CPU、NUMA 与 PCIe 设备的归属关系。图中的上下级表示 hwloc 建模的包含/亲近关系,不表示芯片内部的物理布线
lstopo --of xml topo.xml--of xml 输出 hwloc 的结构化 XML保存完整拓扑属性,便于脚本解析、跨机器比较或在另一台机器上重新绘图

静态拓扑只说明“可以怎样放置”,运行时还要确认程序“实际放在哪里”:

命令参数含义主要输出与作用
numactl --show--show 显示当前 shell 继承的 NUMA 策略physcpubindcpubindmembindpreferred 等字段,用于检查后续启动的程序会被绑定到哪些 CPU 和内存节点
taskset -cp <PID>-c 用 CPU 列表表示亲和性;-p 查询指定进程输出进程允许运行的逻辑 CPU 列表。<PID> 要替换为实际进程号;多线程程序还需检查各线程的亲和性
numastat -p <PID>-p 按指定进程统计按 NUMA 节点列出进程的 Heap、Stack、Private、Huge 等页面用量和总量,用于发现远端页或页面集中在错误节点的问题
cat /proc/<PID>/numa_maps读取指定进程的内核 NUMA 映射文件每个虚拟内存区域的策略、文件或匿名页类型、N0=... 等各节点页数及页大小。它比汇总值更适合定位某个大数组的实际落点

4.2 PCIe、网卡与 RDMA

命令参数含义主要输出与作用
lspci -Dtv-D 始终显示 PCI domain;-t 树形显示;-v 增加信息输出形如 dddd:bb:ss.f 的 BDF 地址及桥接层级,用于判断设备是否位于同一 PCIe Root Port 下
lspci -Dnnk-D 显示完整 BDF;-nn 同时显示名称和十六进制厂商/设备 ID;-k 显示内核驱动可确认网卡、加速器和 DMA 设备的准确型号、Kernel driver in use 与候选模块
lspci -vv -s <BDF>-vv 输出更详细的能力;-s 只查看指定 BDF 设备重点查看 LnkCap/LnkSta 的链路能力与当前速率/宽度、BAR、MSI/MSI-X 和 NUMA 节点。当前链路低于能力值时可能存在插槽、协商或省电限制
ip -br link-br 使用简洁格式;link 表示查看链路层设备输出网卡名、UP/DOWN 状态、MAC 地址和 MTU,先确定后续 ethtool 要使用的接口名
ethtool <网卡名>网卡名如 eth0,需替换为实际接口输出当前速率、双工模式、自动协商和链路状态;它描述以太网接口,不等同于 RDMA 有效带宽
ethtool -i <网卡名>-i 查询驱动信息驱动、固件版本及 bus-infobus-info 通常可与 lspci 的 BDF 对应起来
ethtool -l <网卡名>-l 查询 channel 数量显示 RX、TX、Other、Combined 队列的最大值与当前值,用于分析收发队列是否能与通信线程并行
rdma dev showdev show 枚举 RDMA 设备RDMA 设备名、节点类型、固件版本和 GUID,用于确认 RDMA 子系统是否识别到 HCA/NIC
rdma link showlink show 按端口显示 RDMA 链路RDMA 设备/端口、状态、物理状态以及关联 netdev,可判断端口是否真正处于可用状态
ibv_devinfo -v-v 输出详细 Verbs 能力传输类型、固件、端口 GUID、活动 MTU、链路层,以及 QP、CQ、MR 等资源上限;这些字段决定注册内存和队列设计的边界
ibdev2netdev无参数,显示 RDMA 端口与 Linux 网卡的映射mlx5_0 port 1 一类 RDMA 名称映射到 ethX/ibX,避免混淆 Verbs 设备名和网络接口名
cat /sys/class/net/<网卡名>/device/numa_node读取网卡 PCI 设备的 NUMA 亲和节点输出一个 NUMA 节点编号;-1 表示内核无法给出可靠归属。通信线程和注册缓冲通常优先放在该节点附近

这组命令形成一条对应关系:网卡名 → 驱动和 BDF → PCIe 层级 → NUMA 亲和节点 → RDMA 端口。只有建立这条映射,才能合理安排通信线程、内存注册和队列。

4.3 DMA、SDMA 与平台设备

命令参数含义主要输出与作用
ls -l /sys/class/dma-l 显示符号链接目标枚举 Linux DMA Engine 框架公开的 channel,并可沿链接找到所属设备。目录为空只说明没有通用 DMA channel 暴露给当前内核
cat /proc/dma读取内核维护的传统 DMA channel 表主要反映遗留 ISA DMA 资源,不是现代 PCIe DMA、SoC SDMA 或 RDMA 设备清单,因此不能用它判断 LX2 是否有 SDMA
`lspci -Dnnkgrep -Ei 'dmadsa
`dmesggrep -Ei 'dmadsa

对 KUPL 这类平台运行时,还要检查其 API 或工具返回的设备数、channel 数、内存注册结果、提交/完成状态以及是否发生 CPU fallback。通用 Linux 命令未发现 SDMA 字样,并不能证明硬件不存在;同样,成功枚举设备也不能证明传输已经走 SDMA,最终需要用 CPU 占用率和传输带宽验证。

4.4 固件、内存设备与性能事件

命令参数含义主要输出与作用
sudo dmidecode -t system -t baseboard -t processor -t memory-t 选择 SMBIOS 类型;这里分别查看整机、主板、处理器和内存厂商/型号、主板信息、CPU 插槽,以及 DIMM 的容量、类型、速率和插槽位置。该信息来自固件,可能不完整或与实际运行频率不同
ls /sys/firmware/acpi/tables/{SLIT,HMAT}{SLIT,HMAT} 是 shell 的花括号展开;ls 仅检查两张表是否存在SLIT 描述 NUMA 节点的相对距离,HMAT 可描述 initiator/target 间的延迟、带宽和内存层级;文件是二进制 ACPI 表,存在并不等于 Linux 已完整采用其数据
lsblk -d -o NAME,MODEL,TRAN,SIZE-d 不展开分区/从设备;-o 指定输出列输出块设备名、型号、传输协议和容量,用于区分本地盘、NVMe 以及其他存储设备
nvme list无参数时枚举 NVMe namespace设备节点、序列号、型号、namespace、使用量、容量和格式,适合确认 NVMe 设备,不用于测量 NUMA 内存带宽
cxl list -M -D -E-M 列出 memory device,-D 列出 decoder,-E 列出 endpoint以结构化数据展示 CXL 内存设备、端点和地址解码关系;只有安装 cxl-cli 且平台公开 CXL 设备时才有有效输出
`perf listgrep -Ei 'uncoreddr
perf stat -a -e <事件1>,<事件2> -- <程序>stat 统计事件;-a 表示全系统;-e 指定事件;-- 分隔 perf 参数和被测程序输出事件计数、运行时间以及计数器复用比例。若只关心单个进程可去掉 -a;事件名称必须来自本机 perf list,并可能受权限限制

探测结果应形成一条可验证的优化链路:

Plain
CPU/NUMA 静态拓扑
-> 进程绑核与页面实际落点
-> PCIe、网卡或 SDMA 的 NUMA 亲和性
-> IMC/Fabric/DMA 性能计数器
-> memcpy、SDMA、MPI 等微基准和完整 kernel 耗时

命令不存在或输出为空,只能说明当前工具、驱动、固件或权限没有公开对应信息,不能单独证明硬件不存在。distance、PCIe 链路能力和理论带宽也都只是线索,最终结论必须由同一绑定条件下的实测结果支持。

最近更新: