使用 PyTorch 在 Windows 上通过 AMD 部署 LLM 入门指南
使用 AMD 消费级显卡,通过 PyTorch 在 Windows 上运行 LLM。
本页内容
MPI 是高性能计算中进程间通信的事实标准。MPI 进程在本地数据上进行计算,同时与其他进程进行大量通信。这使得 MPI 程序能够在具有分布式内存空间的系统上执行,例如集群。MPI 支持不同类型的通信,包括点对点通信和集体通信。点对点通信是基本的通信机制,其中发送进程和接收进程都参与通信。发送方有一个包含消息的缓冲区和一个包含接收方将使用的信息的信封(例如,消息标签、发送方秩号等)。接收方使用信封中的信息来选择指定的消息并将其存储在其接收缓冲区中。在集体通信中,消息可以在一组进程之间交换,而不是仅限于两个进程。集体通信提供了一种便捷、可移植且优化的方式来实现一对多和多对多通信。一些集体通信的例子包括广播、allgather、alltoall 和 allreduce。
如今,许多 MPI 应用程序支持在 GPU 集群上运行。在这些应用程序中,计算密集型代码部分被卸载并加速到 GPU(也称为设备)上。在 MPI 通信方面,MPI 进程需要通信驻留在 GPU 缓冲区中的数据。GPU 感知 MPI 提供了将 GPU 缓冲区传递给 MPI 调用的机会。这消除了程序员通过主机内存暂存 GPU 缓冲区的负担,并使他们能够开发出更具可读性和简洁性的应用程序。此外,通过利用 ROCm RDMA(远程直接内存访问)等加速技术,可以提高应用程序的运行效率。ROCm RDMA 使第三方设备(如 Mellanox Infiniband HCA(主机通道适配器))能够与 GPU 内存建立直接的对等数据路径,而无需主机干预。包括 OpenMPI、MVAPICH2 和 Cray MPICH 在内的最知名的 MPI 实现都支持 GPU 感知通信。
以下代码显示了一个简单的 GPU 感知点对点通信示例
#include <stdio.h>#include <hip/hip_runtime.h>#include <mpi.h>
int main(int argc, char **argv) { int i,rank,size,bufsize; int *h_buf; int *d_buf; MPI_Status status;
bufsize=100;
MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size);
//allocate buffers h_buf=(int*) malloc(sizeof(int)*bufsize); hipMalloc(&d_buf, bufsize*sizeof(int));
//initialize buffers if(rank==0) { for(i=0;i<bufsize;i++) h_buf[i]=i; }
if(rank==1) { for(i=0;i<bufsize;i++) h_buf[i]=-1; }
hipMemcpy(d_buf, h_buf, bufsize*sizeof(int), hipMemcpyHostToDevice);
//communication if(rank==0) MPI_Send(d_buf, bufsize, MPI_INT, 1, 123, MPI_COMM_WORLD);
if(rank==1) MPI_Recv(d_buf, bufsize, MPI_INT, 0, 123, MPI_COMM_WORLD, &status);
//validate results if(rank==1) { hipMemcpy(h_buf, d_buf, bufsize*sizeof(int), hipMemcpyDeviceToHost); for(i=0;i<bufsize;i++) { if(h_buf[i] != i) printf("Error: buffer[%d]=%d but expected %d\n", i, h_buf[i], i); } fflush(stdout); }
//free buffers free(h_buf); hipFree(d_buf);
MPI_Finalize();}从代码中可以看到,我们将 GPU 缓冲区 (d_buf) 传递给 MPI_Send 和 MPI_Recv 调用。该缓冲区使用 hipMalloc 在 GPU 上分配。
要编译和运行此代码,您需要 ROCm 以及您的系统上可用的 GPU 感知 MPI 实现。您可以在 AMD ROCm™ 安装 中找到 ROCm 安装说明。本文档稍后将讨论使用不同 MPI 实现构建和运行上述代码 (gpu-aware.cpp) 的说明。
如前所述,大多数知名的 MPI 实现都支持 GPU 感知通信。在本节中,我们将提供使用 ROCm 支持构建 GPU 感知 OpenMPI 的说明。
要使用 ROCm 支持构建 GPU 感知 OpenMPI,首先需要安装 Unified Communication X (UCX)。UCX 是一个用于高带宽和低延迟网络的通信框架。使用以下命令构建 UCX(版本 1.14)
git clone https://github.com/openucx/ucx.gitcd ucxgit checkout v1.14.x./autogen.sh./configure --prefix=$HOME/.local --with-rocm=/opt/rocm --without-knem --without-cuda --enable-gtest --enable-examplesmake -jmake install成功安装后,UCX 将在 $HOME/.local 目录中可用。现在,我们可以使用以下命令安装带有 ROCm 支持的 GPU 感知 OpenMPI
git clone --recursive -b v5.0.x git@github.com:open-mpi/ompi.gitcd ompi/./autogen.pl./configure --prefix=$HOME/.local --with-ucx=$HOME/.localmake -jmake install使用 OpenMPI 5.0 及更高版本,我们还可以将 —with-rocm=/opt/rocm 添加到 configure 命令中,以利用 Open MPI 中的一些 ROCm 功能,例如派生数据类型、MPI I/O 等。成功安装后,带有 ROCm 支持的 OpenMPI 将在 $HOME/.local 中可用。现在,我们可以按如下方式设置 PATH 和 LD_LIBRARY_PATH
export PATH=$HOME/.local/bin:$PATHexport LD_LIBRARY_PATH=$HOME/.local/lib:$LD_LIBRARY_PATH要使用 OpenMPI 编译 GPU 感知 MPI 程序(例如 gpu-aware.cpp),请设置 OMPI_CC 环境变量以更改 mpicc 包装器编译器以使用 hipcc。然后,您可以使用 mpicc 和 mpirun 分别编译和运行代码
export OMPI_CC=hipccmpicc -o ./gpu-aware ./gpu-aware.cppmpirun -n 2 ./gpu-aware在本节中,我们将讨论如何使用 Cray MPICH 构建和运行 GPU 感知 MPI 程序。首先,请确保您的系统已加载 ROCm、Cray MPICH 和 craype-accel-amd-gfx90a/craype-accel-amd-gfx908 模块。您有两种选择来编译代码
cc -o ./gpu-aware ./gpu-aware.cpp -I/opt/rocm/include/ -L/opt/rocm/lib -lamdhip64 -lhsa-runtime64hipcc -o ./gpu-aware ./gpu-aware.cpp -I/opt/cray/pe/mpich/8.1.18/ofi/cray/10.0/include/ -L/opt/cray/pe/mpich/8.1.18/ofi/cray/10.0/lib -lmpi成功编译后,您可以使用以下命令运行代码
export MPICH_GPU_SUPPORT_ENABLED=1srun -n 2 ./gpu-aware请注意,MPICH_GPU_SUPPORT_ENABLED 设置为 1 以启用 GPU 感知通信。
OSU 微基准测试(OMB)提供了一系列 MPI 基准测试,用于测量各种 MPI 操作的性能,包括点对点、集体、基于主机的和基于设备的通信。在本节中,我们将讨论如何使用 OSU 微基准测试测量设备到设备通信带宽。在本节的实验中,我们使用前面讨论的 OpenMPI 安装。
您可以使用以下命令使用 ROCm 支持构建 OSU 微基准测试
wget https://mvapich.cse.ohio-state.edu/download/mvapich/osu-micro-benchmarks-7.0.1.tar.gztar -xvf osu-micro-benchmarks-7.0.1.tar.gzcd osu-micro-benchmarks-7.0.1./configure --prefix=$HOME/.local/ CC=$HOME/.local/bin/mpicc CXX=$HOME/.local/bin/mpicxx --enable-rocm --with-rocm=/opt/rocmmake -jmake install成功安装后,OMB 将在 $HOME/.local/ 中可用。您可以使用以下命令运行带宽测试
mpirun -n 2 $HOME/.local/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw D D在带宽测试中,发送方进程向接收方发送固定数量的连续消息。在收到所有这些消息后,接收方进程会发送一个回复。此过程重复多次。带宽是根据经过的时间和传输的字节数计算的。命令末尾的 D D 表示我们希望发送和接收缓冲区在设备上分配。
如果您通过上述命令未获得预期的带宽,则可能是 OpenMPI 默认未在使用 UCX。要强制 OpenMPI 使用 UCX,您可以将这些参数添加到 mpirun 命令中
mpirun --mca pml ucx --mca pml_ucx_tls ib,sm,tcp,self,cuda,rocm -np 2 $HOME/.local/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw D D您也可以使用以下命令运行集体通信测试
mpirun -n 4 $HOME/.local/libexec/osu-micro-benchmarks/mpi/collective/osu_allreduce -d rocm上述命令运行四进程 MPI_Allreduce 延迟测试。在此测试中,基准测试测量四进程间 MPI_Allreduce 集体操作的平均延迟,针对各种消息长度,执行大量迭代。-d rocm 指定进程应在 GPU 设备上分配通信缓冲区。
在本节中,我们将讨论具有 AMD Instinct™ MI250 和 AMD Instinct™ MI250X GPU 的系统中的 GPU 到 GPU 通信选项。每个 MI250(X) GPU 由两个图形计算单元 (GCD) 组成。下图显示了一个包含 4 个 MI250 GPU(8 个 GCD)的节点的图。GCD 通过 infinity fabric 链接连接。每个 Infinity fabric 链接的峰值带宽为 50 GB/s。从图中可以看出,GCD 之间的 Infinity fabric 链接数量不同。例如,同一 GPU 上的 GCD 通过 4 个链接连接,而不同 GPU 上的 GCD 通过 1 或 2 个链接连接。从该图还可以观察到 GCD 之间的跃点数量不同。例如,GCD 1 通过一个跃点连接到 GCD 3,而 GCD 0 通过至少两个跃点连接到 GCD 3。GCD 之间可实现的最大带宽取决于 GCD 之间的 Infinity fabric 链接数量以及它们之间的跃点数量。

图 1. 包含 4 个 MI250 GPU(8 个 GCD)的节点图。每个绿色框代表一个具有两个 GCD 的 MI250 GPU。GCD 通过 infinity fabric 链接连接。
GPU 到 GPU 通信有两种选择:1. 使用系统直接内存访问 (SDMA) 引擎,2. 启动内核来处理通信。SDMA 引擎提供了将通信与计算重叠的机会。但是,其缺点是它在 GCD 之间提供的最大带宽为 50 GB/s。相比之下,内核提供更高的通信带宽,但它们需要 CU 来移动数据,因此与内核通信重叠的机会较少。环境变量 HSA_ENABLE_SDMA 可用于在 SDMA 引擎和内核之间进行选择。在 ROCM 5.4 及更低版本中,默认使用 SDMA 引擎。
以下实验 [1] 显示了 GCD 0 与其对等方之间 16MB 消息大小的通信带宽。我们将 HSA_ENABLE_SDMA 设置为 0。因此,启动了一个内核来处理通信。我们设置 HIP_VISIBLE_DEVICES 来为每个实验选择对等 GCD。使用 “-m ((16*1024*1024)):((16*1024*1024))” 我们指定了感兴趣的消息大小,在此示例中为 16MiB。
export HSA_ENABLE_SDMA=0export HIP_VISIBLE_DEVICES=0,1mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 142235.39
export HIP_VISIBLE_DEVICES=0,2mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 38963.65
export HIP_VISIBLE_DEVICES=0,3mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 36903.57
export HIP_VISIBLE_DEVICES=0,4mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 36908.74
export HIP_VISIBLE_DEVICES=0,5mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 34986.54
export HIP_VISIBLE_DEVICES=0,6mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 76276.14
export HIP_VISIBLE_DEVICES=0,7mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 68788.80上述实验表明,对于不同的对等 GCD,我们获得了不同的通信带宽。如前所述,这取决于 GCD 之间的 Infinity Fabric 链接数量以及它们之间的跃点数量。例如,GCD 0 和 1 之间的通信带宽约为 142 GB/s,它们通过四个 Infinity Fabric 链接连接。每个 Infinity Fabric 链接的峰值理论带宽为 50 GB/s。因此,GCD 0 和 1 之间的峰值理论带宽为 200 GB/s。假设可实现带宽约为理论带宽的 70%,则 GCD 0 和 1 之间的通信带宽预计为 142 GB/s。对于 GCD 0 和 2,它们通过一个 Infinity Fabric 链接连接,我们获得的带宽为 38 GB/s。GCD 0 和 3 通过两个跃点连接,因此实现的带宽略低(约为 36 GB/s)。我们可以使用相同的逻辑来解释其他 GCD 对等方的通信带宽。
使用 SDMA 引擎,我们获得的最高带宽约为 50 GB/s,如前所述。
export HSA_ENABLE_SDMA=1export HIP_VISIBLE_DEVICES=0,1mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 49396.52
export HIP_VISIBLE_DEVICES=0,2mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 41925.10
export HIP_VISIBLE_DEVICES=0,3mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 41019.50
export HIP_VISIBLE_DEVICES=0,4mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 42243.36
export HIP_VISIBLE_DEVICES=0,5mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 41870.39
export HIP_VISIBLE_DEVICES=0,6mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 49386.80
export HIP_VISIBLE_DEVICES=0,7mpirun -n 2 $OMB_DIR/libexec/osu-micro-benchmarks/mpi/pt2pt/osu_bw -m $((16*1024*1024)):$((16*1024*1024)) D D# OSU MPI-ROCM Bandwidth Test v7.0# Send Buffer on DEVICE (D) and Receive Buffer on DEVICE (D)# Size Bandwidth (MB/s)16777216 49369.06如果您有任何问题或评论,请在 GitHub 讨论区 与我们联系
[1]
测试使用 ROCm 版本 5.4、UCX 1.14 和 OpenMPI 5.0 进行。本文档中的测试不代表官方性能数据,而是反映了不同通信选项的影响。实际性能取决于系统配置和环境设置。