参考
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-Util 和 Compute 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 的计算模式有以下几种:
- Default:默认模式,允许同时运行图形和计算任务,但不允许两个计算任务同时使用同一个 GPU。
- Exclusive Process:独占模式,只允许一个计算进程使用 GPU,直到该进程结束或放弃 GPU。
- Prohibited:禁止模式,阻止任何计算任务使用 GPU,仅允许图形任务。
- 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