GPU利用率问题MPS
GPU利用率问题MPS

GPU利用率问题MPS

参考

https://docs.nvidia.com/deploy/mps/#cuda-mps-enable-per-ctx-device-multiprocessor-partitioning

https://zw0610.github.io/notes-cn/gpu-sharing-2.html

GPU-Util和 Compute M.

GPU-UtilCompute M. 是在使用 nvidia-smi 命令查看 NVIDIA GPU 状态时常见的两项指标。

GPU-Util

GPU-Util 指的是 GPU 的利用率,它是衡量 GPU 当前负载的一个百分比。当一个 GPU 正在处理计算任务时,它的利用率会增加。如果你看到 GPU-Util 的值接近 100%,那么这意味着 GPU 几乎满负荷工作,可能正在运行图形渲染、深度学习训练、科学计算或其他密集型计算任务。

Compute M.

Compute M. 通常指的是 Compute Mode,即计算模式。这是 GPU 的一个配置属性,决定了 GPU 如何处理并发的计算任务。不同的计算模式影响着 GPU 上可以同时运行的进程数量和类型。

NVIDIA GPU 的计算模式有以下几种:

  1. Default:默认模式,允许同时运行图形和计算任务,但不允许两个计算任务同时使用同一个 GPU。
  2. Exclusive Process:独占模式,只允许一个计算进程使用 GPU,直到该进程结束或放弃 GPU。
  3. Prohibited:禁止模式,阻止任何计算任务使用 GPU,仅允许图形任务。
  4. Exclusive Thread:线程独占模式,允许一个计算任务独占 GPU,直到它释放 GPU 或被其他线程抢占。

通过 nvidia-smi,你可以查看当前 GPU 的计算模式。如果需要改变计算模式,通常需要使用 NVIDIA 的 nvidia-smi 命令行工具,具体命令格式如下:

nvidia-smi -i <GPU-ID> -cm <mode>

其中 <GPU-ID> 是你想要配置的 GPU 的 ID,而 <mode> 是你想要设置的计算模式。例如,要将 GPU 0 设置为 Exclusive Process 模式,可以使用:

nvidia-smi -i 0 -cm 1

但是,请注意,改变计算模式可能需要管理员权限,并且在某些情况下,可能需要重启系统才能生效。

MPS

与单核CPU的调度方式类似,在单一时间片内,GPU中只会有一个GPU进程在运行,当多个进程同时把CUDA任务发射到GPU时,GPU使用时间片轮转调度的方式,多个GPU进程之间在微观层面上是交替运行的。这也导致,在某一个时间片内,如果正在运行的GPU进程没有很好地利用计算资源,那么空闲的计算资源就是浪费掉的。也就是说,GPU并没有真正地进行并发计算。再加上不同进程的上下文切换,也带来了更多的时间开销。

Nvidia针对多进程并发执行的场景推出了多进程服务解决方案-MPS,该方案可以做到空分复用。

MPS的运行模式为一个MPS Server和多个MPS Client。MPS Server通过一个CUDA Context管理GPU硬件资源,每个MPS Client对应一个GPU进程,多个MPS Client会将它们的任务通过MPS Server传入GPU,MPS Server可以把多个进程的上下文进行融合,合并后的进程将多个进程的Kernel交织到一起进行发射,从而越过了硬件时间分片调度的限制,使得它们的CUDAkernels实现真正意义上的并行,这可以带来以下好处:

> 进程之间无需上下文切换,减少了上下文切换的开销。

> 同一个时间片里,多个进程的kernel一起执行,提升了GPU计算资源的利用率。

MPS在单进程对GPU利用率不高的情况下是非常有用的,MPS的缺点则在于故障隔离问题,本文忽略。

终止客户端应用程序的通用工作流:

使用 control 命令获取当前活动 MPS 客户端的状态

$ echo "ps" | nvidia-cuda-mps-control

PID ID SERVER DEVICE NAMESPACE COMMAND

9741 0 6472 GPU-cb1213a3-d6a4-be7f 4026531836 ./nbody

9743 0 6472 GPU-cb1213a3-d6a4-be7f 4026531836 ./matrixMul

使用主机 PID 命名空间中的 PID 终止,如下所述:

$ echo "terminate_client 6472 9741" | nvidia-cuda-mps-control

#wait until terminate_client to return

#upon successful termination 0 is returned

0

现在kill是安全的:

$ kill -9 9741

Tegra 平台不支持 MPS 客户端终止。

export CUDA_MPS_ENABLE_PER_CTX_DEVICE_MULTIPROCESSOR_PARTITIONING=1
export CUDA_MPS_ACTIVE_THREAD_PERCENTAGE=70

nvidia-cuda-mps-控制

此控件守护程序通常存储在 Linux 系统下,通常以超级用户权限运行,用于管理下一节中描述的内容。以下是相关用例:/usr/binnvidia-cuda-mps-server

man nvidia-cuda-mps-control          # Describes usage of this utility.

nvidia-cuda-mps-control -d           # Start daemon in background process.

ps -ef | grep mps                    # Check if the MPS daemon is running.

echo quit | nvidia-cuda-mps-control  # Shut the daemon down.

nvidia-cuda-mps-control -f           # Start daemon in foreground.

nvidia-cuda-mps-control -v           # Print version of control daemon executable (applicable on Tegra platforms only).

nvidia-smi

man nvidia-smi                        # Describes usage of this utility.
nvidia-smi -L                         # List the GPU's on node.
nvidia-smi -q                         # List GPU state and configuration information.
nvidia-smi -q -d compute              # Show the compute mode of each GPU.
nvidia-smi -i 0 -c EXCLUSIVE_PROCESS  # Set GPU 0 to exclusive mode, run as root.
nvidia-smi -i 0 -c DEFAULT            # Set GPU 0 to default mode, run as root. (SHARED_PROCESS)
nvidia-smi -i 0 -r                    # Reboot GPU 0 with the new setting.

控制守护程序创建一个文件,其中包含控制守护程序进程的 PID 这。当有多个并行运行的控制守护程序实例时,一

demo

使用pytorch编写一个简单的训练任务,且能较长时间、较大占用地利用GPU:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
import torch.optim as optim

# 设置随机种子以获得可重复的结果
torch.manual_seed(0)

# 创建一个简单的全连接神经网络
class SimpleNet(nn.Module):
    def __init__(self):
        super(SimpleNet, self).__init__()
        self.fc1 = nn.Linear(1000, 512)
        self.fc2 = nn.Linear(512, 256)
        self.fc3 = nn.Linear(256, 10)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

# 实例化模型
model = SimpleNet().cuda()   # 将模型移动到 GPU 上

# 设置损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# 设置训练参数
batch_size = 512*5
num_epochs = 1000*1000*1000
data_dim = 1000
num_classes = 10

# 创建随机数据
inputs = torch.randn(batch_size, data_dim).cuda()
labels = torch.randint(0, num_classes, (batch_size,)).cuda()

# 训练循环
for epoch in range(num_epochs):
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 100 == 0:
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

print('Finished Training')
CUDA_VISIBLE_DEVICES=2 python app.py --avatar_id avator_1 --model musetalk --batch_size 8 --listenport 8056
CUDA_VISIBLE_DEVICES=2 python app2.py --avatar_id avator_1 --model musetalk --batch_size 8 --listenport 8057

发表回复

您的电子邮箱地址不会被公开。