使用 CUDA 以编程方式找出 GPU 链接拓扑
Figuring out GPU links topology programmatically with CUDA
我正在尝试找出 GPU 之间的 link 拓扑结构。
基本上,做与 nvidia-smi topo -m
几乎相同的事情。
我找到了一个 CUDA 示例 topologyQuery
,它基本上调用
cudaDeviceGetP2PAttribute(&perfRank, cudaDevP2PAttrPerformanceRank, device1, device2)
每对 GPU。
运行 这个例子的结果(我稍微修改了输出表示)让我很困惑(在与同一台机器上 nvidia-smi topo -m
的结果比较之后):
$ ./topologyQuery
X 1 1 0 0 0 0 0
1 X 0 1 0 0 0 0
1 0 X 0 0 0 1 0
0 1 0 X 0 0 0 1
0 0 0 0 X 1 1 0
0 0 0 0 1 X 0 1
0 0 1 0 1 0 X 0
0 0 0 1 0 1 0 X
$ nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity
GPU0 X NV1 NV1 NV2 NV2 PHB PHB PHB 0-95
GPU1 NV1 X NV2 NV1 PHB NV2 PHB PHB 0-95
GPU2 NV1 NV2 X NV2 PHB PHB NV1 PHB 0-95
GPU3 NV2 NV1 NV2 X PHB PHB PHB NV1 0-95
GPU4 NV2 PHB PHB PHB X NV1 NV1 NV2 0-95
GPU5 PHB NV2 PHB PHB NV1 X NV2 NV1 0-95
GPU6 PHB PHB NV1 PHB NV1 NV2 X NV2 0-95
GPU7 PHB PHB PHB NV1 NV2 NV1 NV2 X 0-95
来自https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__DEVICE.html:
cudaDevP2PAttrPerformanceRank: A relative value indicating the
performance of the link between two devices. Lower value means better
performance (0 being the value used for most performant link).
为什么 NV1 获得了排名 1?为什么 PHB 排名为 0?
我是否误解了 cudaDevP2PAttrPerformanceRank
查询的目的?
我不知道你测试的是什么系统(它看起来大约像 DGX-1 的输出)。
关于这个问题:
Why PHB got rank 0?
如果您 运行 原始 topologyQuery
示例代码,您会看到(至少在类似 DGX-1 的系统上)它不会打印出每个 GPU 对的性能排名。据我所见,它不会为指示 PHB
的地方打印出性能等级。如果您研究原始代码,原因很清楚:这些对组合不支持 P2P。但是,在这些情况下,您的代码似乎打印出一个零。所以我想说,与原始 topologyQuery
代码相比,这是您的代码中的一个缺陷,它导致了这个问题和您的误解。 PHB
原始代码未分配等级 0。但是您修改后的代码可以做到这一点。那就等你来回答吧。
Why NV1 got rank 1?
关于其余部分,NV2
连接意味着这 2 个 GPU 之间的双 link NVLink 连接(每个方向 50GB/s)。这将是性能最高的 link 类型(在该特定系统中),因此它的 link 值为 0。
NV1
连接意味着单个 link NVLink 连接(每个方向 25GB/s)。这将比 NV2
性能低,因此它被指定为 link 性能值 1。增加性能数字表示降低 link 性能。
顺便说一句,如果您打算这样做:
Basically, do pretty much the same nvidia-smi topo -m
does.
您将无法通过 CUDA API 调用严格做到这一点。
作为参考,这里是 DGX-1 的 nvidia-smi topo -m
输出和 ./topologyQuery
输出:
# nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity
GPU0 X NV1 NV1 NV2 NV2 PHB PHB PHB 0-79
GPU1 NV1 X NV2 NV1 PHB NV2 PHB PHB 0-79
GPU2 NV1 NV2 X NV2 PHB PHB NV1 PHB 0-79
GPU3 NV2 NV1 NV2 X PHB PHB PHB NV1 0-79
GPU4 NV2 PHB PHB PHB X NV1 NV1 NV2 0-79
GPU5 PHB NV2 PHB PHB NV1 X NV2 NV1 0-79
GPU6 PHB PHB NV1 PHB NV1 NV2 X NV2 0-79
GPU7 PHB PHB PHB NV1 NV2 NV1 NV2 X 0-79
Legend:
X = Self
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
PXB = Connection traversing multiple PCIe switches (without traversing the PCIe Host Bridge)
PIX = Connection traversing a single PCIe switch
NV# = Connection traversing a bonded set of # NVLinks
# ./topologyQuery
GPU0 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 1
GPU0 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 1
GPU0 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 0
GPU0 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 0
GPU1 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 1
GPU1 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 0
GPU1 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 1
GPU1 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 1
GPU2 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 1
GPU3 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 0
GPU3 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 1
GPU3 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 0
GPU3 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 0
GPU4 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 1
GPU5 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 0
GPU6 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 0
GPU7 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 1
GPU7 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 0
GPU7 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 1
GPU7 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 0
GPU0 <-> CPU:
* Atomic Supported: no
GPU1 <-> CPU:
* Atomic Supported: no
GPU2 <-> CPU:
* Atomic Supported: no
GPU3 <-> CPU:
* Atomic Supported: no
GPU4 <-> CPU:
* Atomic Supported: no
GPU5 <-> CPU:
* Atomic Supported: no
GPU6 <-> CPU:
* Atomic Supported: no
GPU7 <-> CPU:
* Atomic Supported: no
我正在尝试找出 GPU 之间的 link 拓扑结构。
基本上,做与 nvidia-smi topo -m
几乎相同的事情。
我找到了一个 CUDA 示例 topologyQuery
,它基本上调用
cudaDeviceGetP2PAttribute(&perfRank, cudaDevP2PAttrPerformanceRank, device1, device2)
每对 GPU。
运行 这个例子的结果(我稍微修改了输出表示)让我很困惑(在与同一台机器上 nvidia-smi topo -m
的结果比较之后):
$ ./topologyQuery
X 1 1 0 0 0 0 0
1 X 0 1 0 0 0 0
1 0 X 0 0 0 1 0
0 1 0 X 0 0 0 1
0 0 0 0 X 1 1 0
0 0 0 0 1 X 0 1
0 0 1 0 1 0 X 0
0 0 0 1 0 1 0 X
$ nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity
GPU0 X NV1 NV1 NV2 NV2 PHB PHB PHB 0-95
GPU1 NV1 X NV2 NV1 PHB NV2 PHB PHB 0-95
GPU2 NV1 NV2 X NV2 PHB PHB NV1 PHB 0-95
GPU3 NV2 NV1 NV2 X PHB PHB PHB NV1 0-95
GPU4 NV2 PHB PHB PHB X NV1 NV1 NV2 0-95
GPU5 PHB NV2 PHB PHB NV1 X NV2 NV1 0-95
GPU6 PHB PHB NV1 PHB NV1 NV2 X NV2 0-95
GPU7 PHB PHB PHB NV1 NV2 NV1 NV2 X 0-95
来自https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__DEVICE.html:
cudaDevP2PAttrPerformanceRank: A relative value indicating the performance of the link between two devices. Lower value means better performance (0 being the value used for most performant link).
为什么 NV1 获得了排名 1?为什么 PHB 排名为 0?
我是否误解了 cudaDevP2PAttrPerformanceRank
查询的目的?
我不知道你测试的是什么系统(它看起来大约像 DGX-1 的输出)。
关于这个问题:
Why PHB got rank 0?
如果您 运行 原始 topologyQuery
示例代码,您会看到(至少在类似 DGX-1 的系统上)它不会打印出每个 GPU 对的性能排名。据我所见,它不会为指示 PHB
的地方打印出性能等级。如果您研究原始代码,原因很清楚:这些对组合不支持 P2P。但是,在这些情况下,您的代码似乎打印出一个零。所以我想说,与原始 topologyQuery
代码相比,这是您的代码中的一个缺陷,它导致了这个问题和您的误解。 PHB
原始代码未分配等级 0。但是您修改后的代码可以做到这一点。那就等你来回答吧。
Why NV1 got rank 1?
关于其余部分,NV2
连接意味着这 2 个 GPU 之间的双 link NVLink 连接(每个方向 50GB/s)。这将是性能最高的 link 类型(在该特定系统中),因此它的 link 值为 0。
NV1
连接意味着单个 link NVLink 连接(每个方向 25GB/s)。这将比 NV2
性能低,因此它被指定为 link 性能值 1。增加性能数字表示降低 link 性能。
顺便说一句,如果您打算这样做:
Basically, do pretty much the same
nvidia-smi topo -m
does.
您将无法通过 CUDA API 调用严格做到这一点。
作为参考,这里是 DGX-1 的 nvidia-smi topo -m
输出和 ./topologyQuery
输出:
# nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity
GPU0 X NV1 NV1 NV2 NV2 PHB PHB PHB 0-79
GPU1 NV1 X NV2 NV1 PHB NV2 PHB PHB 0-79
GPU2 NV1 NV2 X NV2 PHB PHB NV1 PHB 0-79
GPU3 NV2 NV1 NV2 X PHB PHB PHB NV1 0-79
GPU4 NV2 PHB PHB PHB X NV1 NV1 NV2 0-79
GPU5 PHB NV2 PHB PHB NV1 X NV2 NV1 0-79
GPU6 PHB PHB NV1 PHB NV1 NV2 X NV2 0-79
GPU7 PHB PHB PHB NV1 NV2 NV1 NV2 X 0-79
Legend:
X = Self
SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
PXB = Connection traversing multiple PCIe switches (without traversing the PCIe Host Bridge)
PIX = Connection traversing a single PCIe switch
NV# = Connection traversing a bonded set of # NVLinks
# ./topologyQuery
GPU0 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 1
GPU0 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 1
GPU0 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 0
GPU0 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 0
GPU1 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 1
GPU1 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 0
GPU1 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 1
GPU1 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 1
GPU2 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 0
GPU2 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 1
GPU3 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 0
GPU3 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 1
GPU3 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 0
GPU3 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU0:
* Atomic Supported: yes
* Perf Rank: 0
GPU4 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 1
GPU4 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU1:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 1
GPU5 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 0
GPU5 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU2:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 1
GPU6 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 0
GPU6 <-> GPU7:
* Atomic Supported: yes
* Perf Rank: 0
GPU7 <-> GPU3:
* Atomic Supported: yes
* Perf Rank: 1
GPU7 <-> GPU4:
* Atomic Supported: yes
* Perf Rank: 0
GPU7 <-> GPU5:
* Atomic Supported: yes
* Perf Rank: 1
GPU7 <-> GPU6:
* Atomic Supported: yes
* Perf Rank: 0
GPU0 <-> CPU:
* Atomic Supported: no
GPU1 <-> CPU:
* Atomic Supported: no
GPU2 <-> CPU:
* Atomic Supported: no
GPU3 <-> CPU:
* Atomic Supported: no
GPU4 <-> CPU:
* Atomic Supported: no
GPU5 <-> CPU:
* Atomic Supported: no
GPU6 <-> CPU:
* Atomic Supported: no
GPU7 <-> CPU:
* Atomic Supported: no