使用 NVIDIA MPS 运行 TorchServe¶
为了部署 ML 模型,TorchServe 在单独的进程中启动每个工作进程,从而将每个工作进程与其他进程隔离。每个进程创建自己的 CUDA 上下文来执行其内核并访问分配的内存。
虽然 NVIDIA GPU 在其默认设置中允许在单个设备上运行多个进程来运行 CUDA 内核,但这涉及以下缺点
内核的执行通常是串行的
每个进程创建自己的 CUDA 上下文,这会占用额外的 GPU 内存
对于这些情况,NVIDIA 提供了多进程服务 (MPS),它可以
允许多个进程在同一 GPU 上共享同一 CUDA 上下文
并行运行其内核
这可能会导致
当在同一 GPU 上使用多个工作进程时,性能会提高
由于共享上下文,GPU 内存利用率降低
为了利用 NVIDIA MPS 的优势,我们需要在启动 TorchServe 本身之前使用以下命令启动 MPS 守护程序。
sudo nvidia-smi -c 3
nvidia-cuda-mps-control -d
第一个命令为 GPU 启用独占处理模式,仅允许一个进程(MPS 守护程序)使用它。第二个命令启动 MPS 守护程序本身。要关闭守护程序,我们可以执行
echo quit | nvidia-cuda-mps-control
有关 MPS 的更多详细信息,请参阅 NVIDIA 的 MPS 文档。应该注意的是,由于硬件资源有限,MPS 仅允许 48 个进程(对于 Volta GPU)连接到守护程序。向同一 GPU 添加更多客户端/工作进程将导致失败。
基准¶
为了展示 TorchServe 在激活 MPS 后的性能,并帮助您决定是否为部署启用 MPS,我们将使用具有代表性的工作负载执行一些基准测试。
首先,我们想研究在不同操作点激活 MPS 后,工作进程的吞吐量如何演变。作为基准测试的示例工作负载,我们选择了 HuggingFace Transformers 序列分类示例。我们在 AWS 上的 g4dn.4xlarge 以及 p3.2xlarge 实例上执行基准测试。这两种实例类型都为每个实例提供一个 GPU,这将导致多个工作进程计划在同一 GPU 上。对于基准测试,我们专注于由 benchmark-ab.py 工具测量的模型吞吐量。
首先,我们测量单个工作进程在不同批次大小下的吞吐量,因为它将向我们展示 GPU 的计算资源何时被完全占用。其次,我们测量在预期 GPU 仍有一些资源可共享的批次大小下,部署两个工作进程的吞吐量。对于每个基准测试,我们执行五次运行并取运行的中位数。
我们使用以下 config.json 进行基准测试,仅相应地覆盖工作进程的数量和批次大小。
{
"url":"/home/ubuntu/serve/examples/Huggingface_Transformers/model_store/BERTSeqClassification",
"requests": 10000,
"concurrency": 600,
"input": "/home/ubuntu/serve/examples/Huggingface_Transformers/Seq_classification_artifacts/sample_text_captum_input.txt",
"workers": "1"
}
请注意,我们将并发级别设置为 600,这将确保 TorchServe 内部的批次聚合将批次填充到最大批次大小。但与此同时,这将歪曲延迟测量,因为许多请求将在队列中等待处理。因此,我们将在以下内容中忽略延迟测量。
G4 实例¶
我们首先对 G4 实例执行单工作进程基准测试。在下面的图中,我们看到,对于最大为 4 的批次大小,我们看到吞吐量随着批次大小的增加而稳步增加。
接下来,我们将工作进程的数量增加到两个,以便比较在运行和不运行 MPS 的情况下的吞吐量。为了为第二组运行启用 MPS,我们首先为 GPU 设置独占处理模式,然后启动 MPS 守护程序,如上所示。
我们根据之前的发现选择介于 1 和 8 之间的批次大小。在图中,我们可以看到,对于批次大小 1 和 8,吞吐量方面的性能可能会更好(高达 +18%),而对于其他批次大小,性能可能会更差(-11%)。对此结果的一种解释可能是,当我们在一个工作进程中运行 BERT 模型时,G4 实例没有太多资源可共享。
P3 实例¶
接下来,我们将使用更大的 p3.2xlarge 实例运行相同的实验。对于单个工作进程,我们获得以下吞吐量值
我们可以看到吞吐量稳步增加,但对于大于 8 的批次大小,我们看到了收益递减。最后,我们在 P3 实例上部署了两个工作进程,并比较了在运行和不运行 MPS 的情况下运行它们的情况。我们可以看到,对于 1 到 32 之间的批次大小,启用 MPS 的吞吐量始终更高(高达 +25%),批次大小为 16 的情况除外。
总结¶
在上一节中,我们看到,通过为运行同一模型的两个工作进程启用 MPS,我们收到了好坏参半的结果。对于较小的 G4 实例,我们仅在某些操作点看到了好处,而对于较大的 P3 实例,我们看到了更一致的改进。这表明,在吞吐量方面,运行 MPS 部署的好处在很大程度上取决于工作负载和环境,需要使用适当的基准测试和工具针对特定情况进行确定。应该注意的是,之前的基准测试仅关注吞吐量,而忽略了延迟和内存占用。由于使用 MPS 只会创建一个 CUDA 上下文,因此可以将更多工作进程打包到同一 GPU 中,这也需要在相应的场景中加以考虑。