快捷方式

使用 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 的批次大小,我们看到吞吐量随着批次大小的增加而稳步增加。

G4 benchmark, single worker

接下来,我们将工作进程的数量增加到两个,以便比较在运行和不运行 MPS 的情况下的吞吐量。为了为第二组运行启用 MPS,我们首先为 GPU 设置独占处理模式,然后启动 MPS 守护程序,如上所示。

我们根据之前的发现选择介于 1 和 8 之间的批次大小。在图中,我们可以看到,对于批次大小 1 和 8,吞吐量方面的性能可能会更好(高达 +18%),而对于其他批次大小,性能可能会更差(-11%)。对此结果的一种解释可能是,当我们在一个工作进程中运行 BERT 模型时,G4 实例没有太多资源可共享。

G4 benchmark, two workers

P3 实例

接下来,我们将使用更大的 p3.2xlarge 实例运行相同的实验。对于单个工作进程,我们获得以下吞吐量值

P3 benchmark, single worker

我们可以看到吞吐量稳步增加,但对于大于 8 的批次大小,我们看到了收益递减。最后,我们在 P3 实例上部署了两个工作进程,并比较了在运行和不运行 MPS 的情况下运行它们的情况。我们可以看到,对于 1 到 32 之间的批次大小,启用 MPS 的吞吐量始终更高(高达 +25%),批次大小为 16 的情况除外。

P3 benchmark, two workers

总结

在上一节中,我们看到,通过为运行同一模型的两个工作进程启用 MPS,我们收到了好坏参半的结果。对于较小的 G4 实例,我们仅在某些操作点看到了好处,而对于较大的 P3 实例,我们看到了更一致的改进。这表明,在吞吐量方面,运行 MPS 部署的好处在很大程度上取决于工作负载和环境,需要使用适当的基准测试和工具针对特定情况进行确定。应该注意的是,之前的基准测试仅关注吞吐量,而忽略了延迟和内存占用。由于使用 MPS 只会创建一个 CUDA 上下文,因此可以将更多工作进程打包到同一 GPU 中,这也需要在相应的场景中加以考虑。

文档

访问 PyTorch 的全面开发者文档

查看文档

教程

获取面向初学者和高级开发者的深入教程

查看教程

资源

查找开发资源并获得解答

查看资源