3 月 19 日下午 2 点,锁定 NVIDIA AI 网络中文专场。立即注册观看
数据中心/云端

使用 CRIU 实现 CUDA 应用程序检查点

CUDA 的检查点和恢复功能通过名为 cuda-checkpoint 的命令行实用程序公开,该实用程序可用于在正在运行的 Linux 进程中以透明方式检查点和恢复 CUDA 状态,同时也可以与开源检查点实用程序 CRIU(用户空间中的检查点/恢复)相结合,以完全检查点 CUDA 应用程序。

检查点概述

透明的每进程检查点在虚拟机检查点和应用程序驱动检查点之间提供了一个中间地带。每进程检查点可与容器结合使用,检查复杂应用程序的状态,从而促进诸如以下用例。

  • 容错,带有定期检查点
  • 通过检查抢占性任务,抢占单节点上低优先级工作的先机
  • 通过迁移实现集群调度
Under Virtual machine checkpointing, icons show a vm with physical memory, storage and hardware state. Under Per-process checkpointing, icons show a process with threads, virtual memory, and file descriptors open to a terminal and device. Under Application-driven checkpointing, an icon shows an application’s state in grid size and a list of three coordinates: mines clicks and flags.
图 1.检查点类型

CRIU

CRIU(用户空间中的检查点/恢复)是适用于 Linux 的开源检查点实用程序,在 NVIDIA 外部进行维护,可以检查点和恢复进程树。

CRIU 通过一个名为criu并通过检查点和恢复与进程关联的每个内核模式资源来运行。这些资源包括:

  • 匿名内存
  • 线程
  • 常规文件
  • 插槽
  • 检查点进程之间的管道

由于这些资源的行为由 Linux 指定,并且独立于底层硬件,CRIU 知道如何检查并恢复这些资源。

相比之下,NVIDIA GPU 提供的功能超出了标准 Linux 内核的功能,因此 CRIU 无法管理它们。cuda-checkpoint 增加了这项功能,可以与 CRIU 一起用于检查点和恢复 CUDA 应用程序。

CUDA 检查点

cuda-checkpoint 检查点并恢复单个 Linux 进程的 CUDA 状态。它支持显示驱动程序版本 550 及更高版本,可以从 /bin 目录 下载。

localhost$ cuda-checkpoint --help

CUDA checkpoint and restore utility.
Toggles the state of CUDA within a process between suspended and running.
Version 550.54.09. Copyright (C) 2024 NVIDIA Corporation. All rights reserved.

    --toggle --pid <value>
        Toggle the state of CUDA in the specified process.

    --help
        Print help message.

cuda-checkpoint 二进制文件可以在挂起和运行之间切换由 PID 指定的进程的 CUDA 状态,一种从运行到挂起的过渡称为挂起,相反的过渡称为恢复

进程的 CUDA 状态最初正在运行。当cuda-checkpoint在用于在进程中挂起 CUDA 时,它遵循以下步骤:

请提供 Input Content,我将根据 Source Content 对其进行校对和修改,以确保内容的正确性和清晰性。Lock icon. 任何启动工作、管理资源或以其他方式影响 GPU 状态的 CUDA 驱动程序 API 均会被锁定。
请提供 Input Content,我将对其进行润色和校对。Crescent moon icon. 已提交的 CUDA 工作 (包括流回调) 已完成。
提供输入内容。 设备内存复制到主机,再复制到由 CUDA 驱动程序管理的分配中。
请提供 Input Content,我将根据 Source Content 对其进行校对和修改,以确保句子表达正确、意思明确。Circle icon with rectangle at the bottom. 所有 CUDA GPU 资源均已发布。
表 1、cuda-checkpoint用于挂起 CUDA

cuda-checkpoint 不会挂起 CPU 线程,这些线程可以继续安全地与 CUDA 交互,方式如下:调用运行时或驱动程序 API,这些 API 可能会阻塞直到 CUDA 恢复;或访问由 cudaMallocHost 和类似 API 分配的主机内存,这些内存仍然有效。

挂起的 CUDA 进程不再直接指操作系统级别的任何 GPU 硬件,因此可能会由 CPU 检查点实用程序 (例如 CRIU) 进行检查点检查。

当进程的 CUDA 状态恢复时,使用cuda-checkpoint它遵循以下步骤:

请提供 Input Content,我将对其进行润色和校对。 该过程会重新获取 GPU。
请提供 Input Content,我将对其进行修改和润色,以确保其语句正确、意思明确。Pages icon with arrow pointing left-to-right. 系统会将设备内存复制回 GPU,并将 GPU 内存映射恢复到原始地址。
请提供 Input Content,我将根据 Source Content 对其进行校对和修改,以确保语言流畅、意思明确。Sun icon 流和上下文等 CUDA 对象将被恢复。
请提供 Input Content,我将根据 Source Content 对其进行校对和修改,以确保内容的正确性和清晰性。Unlocked lock icon CUDA 驱动程序 API 已解锁。
表 2.CUDA 状态使用cuda-checkpoint

此时,CUDA 会调用 unblock,CUDA 即可重新开始在 GPU 上运行。

检查点示例

此示例使用 cuda-checkpoint 和 CRIU 来检查名为 counter 的 CUDA 应用程序。每当 counter 收到数据包时,它都会增加 GPU 内存并使用更新后的值进行回复。示例代码也可以在 GitHub 库中找到。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 10000

__device__ int counter = 100;
__global__ void increment()
{
    counter++;
}

int main(void)
{
    cudaFree(0);

    int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    sockaddr_in addr = {AF_INET, htons(PORT), inet_addr("127.0.0.1")};
    bind(sock, (sockaddr *)&addr, sizeof addr);

    while (true) {
        char buffer[16] = {0};
        sockaddr_in peer = {0};
        socklen_t inetSize = sizeof peer;
        int hCounter = 0;

        recvfrom(sock, buffer, sizeof buffer, 0, (sockaddr *)&peer, &inetSize);

        increment<<<1,1>>>();
        cudaMemcpyFromSymbol(&hCounter, counter, sizeof counter);

        size_t bytes = sprintf(buffer, "%d\n", hCounter);
        sendto(sock, buffer, bytes, 0, (sockaddr *)&peer, inetSize);
    }
    return 0;
}

您可以构建counter 应用程序,nvcc.

localhost$ nvcc counter.cu -o counter

保存counter供后续命令参考的 PID:

localhost# PID=$!

发送counter数据包并观察返回值。初始值为 100,但响应为 101,表明 GPU 显存自初始化以来发生了变化。

localhost# echo hello | nc -u localhost 10000 -W 1
101

使用nvidia-smi确认counter在 GPU 上运行:

localhost# nvidia-smi --query --display=PIDS | grep $PID
Process ID : 298027

使用cuda-checkpoint暂停counterCUDA 状态:

localhost# cuda-checkpoint --toggle --pid $PID

使用nvidia-smi确认counter不再在 GPU 上运行:

localhost# nvidia-smi --query --display=PIDS | grep $PID

创建用于保存检查点图像的目录:

localhost# mkdir -p demo

使用criu检查点counter:

localhost# criu dump --shell-job --images-dir demo --tree $PID
[1]+ Killed ./counter

确认counter不再运行:

localhost# ps --pid $PID
PID TTY TIME CMD

使用criu来恢复counter:

localhost# criu restore --shell-job --restore-detached --images-dir demo

使用cuda-checkpoint继续counterCUDA 状态:

localhost# cuda-checkpoint --toggle --pid $PID

现在counter然后向其发送另一个数据包。响应为 102,表明先前的 GPU 操作已正确持久化。

localhost# echo hello | nc -u localhost 10000 -W 1
102

功能

截至显示驱动版本 550,检查点和恢复功能仍在积极开发中。特别是cuda-checkpoint具有以下特征:

  • 仅限 x64、
  • 作用于单个进程而非进程树。
  • 不支持 UVM 或 IPC 显存。
  • 不支持 GPU 迁移。
  • 等待已提交的 CUDA 工作完成,然后再完成检查点。
  • 如果在检查点或恢复期间遇到错误 (例如存在 UVM 分配),请勿尝试保持进程处于良好状态。

这些限制将在后续的显示驱动程序版本中解决,并且不需要更新 cuda-checkpoint 实用程序本身。cuda-checkpoint 实用程序公开了驱动程序中包含的功能。

总结

cuda-checkpoint 实用程序与 CRIU 结合后,可实现 Linux 应用程序的每进程透明检查点。有关更多信息,请参阅 /NVIDIA/cuda-checkpoint GitHub 存储库。

尝试检查点counter在您自己的机器上运行应用程序或任何其他兼容的 CUDA 应用程序!

 

标签