设备 API#
本节介绍在使用 NVSHMEM 运行时开发应用程序时,一些关键的设备 API 注意事项。
设备 API 在对等传输上的注意事项#
某些应用程序可以通过增加传输感知来提高设备 API 调用的性能。对于对等 (P2P) 传输,设备 API 将使用 GPU 的流式多处理器 (SM) 来移动数据。通过在线程块池中平衡每个 GPU 线程的工作,可以实现最佳性能。与单个线程块相比,使用多个线程块可以提高数据移动的效率,单个线程块不足以饱和 NVIDIA (R) NVLink (R) 上的对等通信带宽。标准的 NVSHMEM API 使用一个 GPU 线程来执行数据移动。当移动大量数据时,单个线程可能不足以进行对等通信,因此应用程序应使用此 API 的 warp 或 block 变体,例如 nvshmemx_TYPENAME_put_block
或 nvshmemx_TYPENAME_put_warp
,而不是 nvshmem_TYPENAME_put
。例如,下表描述了尝试通过 P2P 传输将 128 个元素的块从 GPU A 移动到 GPU B 时,这些 API 之间的行为差异。
比较参数 |
|
|
建议 |
---|---|---|---|
内部同步 |
此 API 的结尾/开头将包含 |
对于延迟参数,优先选择 |
|
内存存储操作数(ops) |
32 x 16B 存储,因为内部优先使用 16B 对齐存储(>> 8B > 4B > 2B > 1B)以尽可能高效地移动数据 |
128 x 4B 存储 |
优先选择 |
自动传输合并 |
是 |
是 |
使用任何一个,用于块中连续元素的自动传输合并 |
使用此传输的设备 API 的应用程序可以使用 shmem_put_bw 性能基准测试(作为安装的一部分提供)来表征其性能,以确定 GPU 线程 warp 和线程块的数量,从而最大限度地提高 NVLink 利用率/带宽。
应用程序还应通过在使用设备 API 调度数据传输之前,为目标数据分配或选择连续的可寻址内存目标,来利用 P2P 的空间局部性和数据合并功能。例如,如果目标地址在内存中是连续的,则 nvshmem_p
可能对于小消息传输是有效的,因为 P2P 将自动将多个小消息合并为一个大消息传输。
对于此传输,这些 API 的阻塞版本和非阻塞版本 (nvshmemx_TYPENAME_<ops>_nbi
) 的行为是相同的。
设备 API 在基于代理的传输上的注意事项#
某些应用程序可以通过增加传输感知来提高设备 API 调用的性能。对于基于代理的传输(例如 IBRC、UCX、libfabric 等),数据传输将卸载到 NVSHMEM 运行时内部的单个 CPU 代理线程。单个 CPU 代理线程可能不足以驱动远程网络上的小消息传输,因此应用程序必须将尽可能多的数据打包到用于基于代理的传输的大消息中。例如,nvshmem_p
可能对于小消息传输效率不高,因为基于代理的传输不会自动将数据合并为大消息传输。在这种情况下,应用程序应尽可能多地打包数据并使用 nvshmem_put
代替。例如,下表描述了尝试通过基于代理的传输将 128 个元素的块从 GPU A 移动到 GPU B 时,这些 API 之间的行为差异。
比较参数 |
|
|
|
建议 |
---|---|---|---|---|
GPU 线程的所有权 |
NVSHMEM 独占使用 1 个线程,而其余线程可供用户在内核中进行其他操作 |
NVSHMEM 内部使用 1 个线程来发出发送操作,而块中的其余线程在内核中处于空闲状态 |
NVSHMEM 使用 128 个线程,没有线程可供用户在内核中进行其他操作 |
对于关心最大化 GPU 线程所有权以进行内核中的非通信操作的用户,优先选择基于 1 个线程的 |
发送操作数(ops) |
1 x 4B 发送 |
1 x 4B 发送 |
128 x 4B 发送,串行提交命令到 CPU 代理线程 |
为了最大限度地减少发送操作数,优先选择基于 1 个线程或 1 个线程块的 |
使用此传输的设备 API 的应用程序可以使用 ib_write_bw 或 ib_send_lat 性能基准测试(在 linux-rdma/perftest 存储库下可用)来表征峰值带宽/延迟指标。
设备 API 在 IBGDA 传输上的注意事项#
某些应用程序可以通过增加传输感知来提高设备 API 调用的性能。对于 IBGDA 传输,数据传输将卸载到设备上的 GPU 线程。通过使用与多个 GPU 线程关联的多个队列对 (QP)(通常为 1:1 比率),您可以提交多个消息传输,以帮助加速消息速率,使其比基于代理的传输更快。但是,给定消息大小的 IB 带宽可能会成为问题。为了实现此峰值 IB 带宽使用率,并确保 IBGDA 传输在远程通信中有效,应用程序必须将尽可能多的数据打包到大消息中。例如,nvshmem_p
可能对于小消息传输效率不高,因为 IBGDA 传输不会自动将数据合并为大消息传输。在这种情况下,应用程序应尽可能多地打包数据并使用 nvshmem_put
代替。例如,下表描述了尝试通过 IBGDA 传输将 128 个元素的块从 GPU A 移动到 GPU B 时,这些 API 之间的行为差异。
比较参数 |
|
|
|
建议 |
---|---|---|---|---|
GPU 线程的所有权 |
NVSHMEM 独占使用 1 个线程,而其余线程可供用户在内核中进行其他操作 |
NVSHMEM 使用 128 个线程,没有线程可供用户在内核中进行其他操作 |
NVSHMEM 使用 128 个线程,没有线程可供用户在内核中进行其他操作 |
对于关心最大化 GPU 线程所有权以进行内核中的非通信操作的用户,优先选择基于 1 个线程的 |
发送操作数(发送) |
1 x 4B 发送 |
1 x 4B 发送 |
128 x 4B 发送,并行提交命令到一个或多个 QP(由 <CONN>_MAP_BY 定义,CONN=DCI, RC) |
为了最大限度地减少发送操作数,优先选择基于 1 个线程或 1 个线程块的 |
应用程序还可以在运行时修改 QP 到 GPU 线程之间的映射,并使用 NVSHMEM_IBGDA_DCI_MAP_BY 或 NVSHMEM_IBGDA_RC_MAP_BY 环境变量来调整以获得更好的性能。
流上 API#
本节介绍在使用 NVSHMEM 运行时开发应用程序时,一些关键的流上 API 注意事项。
流上 API 作为扩展提供,供用户从主机调用设备端操作,以下 API 功能可能非常有用
如果可用,NVSHMEM 将使用 NVIDIA 集体通信库 (NCCL) 进行流上集体操作。这些集体操作针对主机提交进行了高度优化,建议将现有 NCCL 优化工作负载与点对点 NVSHMEM 操作结合使用的应用程序使用。
流上 API 提供阻塞和非阻塞变体。所有流上 API 相对于主机都是异步的,但非阻塞变体在内部流上排队,并且执行顺序使用
cudaEvent
控制。在两种情况下都保证流顺序。当在内核中发出多个操作时,建议的方法是避免使用设备端
nvshmem_quiet
API。相反,用户应在内核之后调用nvshmemx_quiet_on_stream
。此方法避免了内核中昂贵的网格同步。
主机 API#
本节介绍在使用 NVSHMEM 运行时开发应用程序时,一些关键的主机 API 注意事项。
提供主机 API 是为了完整性,它提供与设备和流上应用程序相同的内存抽象,但与执行 cudaMemcpyAsync
调用或直接从应用程序执行相应的网络操作相比,不提供任何性能优势。主机 API 在代理传输上提供更好的提交性能可能看起来很直观,但 NVSHMEM 仅支持主机 API 的 NVSHMEM_THREAD_SERIALIZED
模式。这意味着应用程序只能从一个主机线程驱动流量,而无需显式同步。