You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为含依赖流与动态参数的CUDA程序应用CUDA Graphs

基于CUDA Graph重构多流依赖的动态参数程序

你的核心需求是把多流依赖、带动态参数的循环迭代转换成CUDA Graph执行,降低启动开销。先指出你现有尝试中的几个关键问题:

  • 手动添加kernel节点完全多余:流捕获已经会自动把kernel<<<>>>、cudaStreamWaitEvent、cudaEventRecord这些操作转换成图节点,重复添加会导致逻辑混乱。
  • 动态参数处理错误:cudaKernelNodeParams.kernelParams需要是指向参数数组的指针,而非直接指向单个参数;迭代变量i作为动态参数,需要和其他参数一起放入数组中更新。
  • 外部事件与图的冲突:原代码中使用的event1、event2是外部事件,在图捕获时应该让CUDA自动生成图内的依赖关系,而非复用外部事件。

正确实现步骤

  1. 初始化迭代:第一次迭代单独执行,完成初始数据准备并记录事件,为后续图迭代建立初始依赖。
  2. 捕获图结构:使用全局捕获模式,执行一次else分支的完整逻辑,让CUDA自动把两个流的操作、流间依赖(通过cudaStreamWaitEvent和cudaEventRecord)捕获到同一个图中。
  3. 动态参数管理:把所有动态参数(i、矩阵指针、数组指针等)放入一个参数数组,每次迭代更新数组内容,再通过cudaGraphExecKernelNodeSetParams更新图中对应kernel节点的参数。
  4. 图执行与资源清理:每次迭代更新参数后启动图执行,循环结束后清理所有CUDA资源。

修正后的完整代码

#include <cuda_runtime.h>
#include <vector>

__global__ void kernel1(int i, int* dynamic_matrix, int* persistent_array) {
    // 核函数实现
}

__global__ void kernel2(int i, int* dynamic_matrix, int* persistent_array) {
    // 核函数实现
}

__global__ void kernel3(int i, int* persistent_array) {
    // 核函数实现
}

void execute_with_cuda_graphs(int* d_matrix, int* d_array, int iter_count) {
    cudaStream_t stream1, stream2;
    cudaEvent_t event1, event2;
    cudaGraph_t graph;
    cudaGraphExec_t graph_exec;
    std::vector<cudaGraphNode_t> kernel_nodes; // 存储图中的kernel节点,用于后续更新参数

    // 初始化CUDA资源
    cudaStreamCreate(&stream1);
    cudaStreamCreate(&stream2);
    cudaEventCreate(&event1);
    cudaEventCreate(&event2);

    // 第一次迭代:初始化
    kernel1<<<1, 512, 0, stream1>>>(0, d_matrix, d_array);
    kernel2<<<1, 512, 0, stream2>>>(0, d_matrix, d_array);
    kernel3<<<1, 512, 0, stream1>>>(0, d_array);
    cudaEventRecord(event1, stream2);
    cudaEventRecord(event2, stream1);
    cudaStreamSynchronize(stream1);
    cudaStreamSynchronize(stream2);

    // 捕获图结构(执行一次else分支逻辑)
    cudaStreamBeginCapture(stream1, cudaStreamCaptureModeGlobal);
    {
        // 流1等待stream2的event1
        cudaStreamWaitEvent(stream1, event1, 0);
        kernel1<<<1, 512, 0, stream1>>>(1, d_matrix, d_array); // 用临时i=1捕获结构

        // 流2等待stream1的event2
        cudaStreamWaitEvent(stream2, event2, 0);
        kernel2<<<1, 512, 0, stream2>>>(1, d_matrix, d_array);
        cudaEventRecord(event1, stream2);

        kernel3<<<1, 512, 0, stream1>>>(1, d_array);
        cudaEventRecord(event2, stream1);
    }
    cudaStreamEndCapture(stream1, &graph);

    // 遍历图节点,收集所有kernel节点
    size_t node_count;
    cudaGraphGetNodes(graph, nullptr, &node_count);
    std::vector<cudaGraphNode_t> all_nodes(node_count);
    cudaGraphGetNodes(graph, all_nodes.data(), &node_count);

    for (auto node : all_nodes) {
        cudaGraphNodeType type;
        cudaGraphNodeGetType(node, &type);
        if (type == cudaGraphNodeTypeKernel) {
            kernel_nodes.push_back(node);
        }
    }

    // 实例化可执行图
    cudaGraphInstantiate(&graph_exec, graph, nullptr, nullptr, 0);

    // 后续迭代:更新参数并执行图
    for (int i = 1; i < iter_count; ++i) {
        // 准备动态参数数组:每个kernel的参数顺序要和核函数定义一致
        // kernel1参数:i, d_matrix, d_array
        void* params_kernel1[] = {&i, &d_matrix, &d_array};
        cudaKernelNodeParams params1 = {
            .func = (void*)kernel1,
            .gridDim = dim3(1),
            .blockDim = dim3(512),
            .sharedMemBytes = 0,
            .kernelParams = params_kernel1,
            .extra = nullptr
        };
        cudaGraphExecKernelNodeSetParams(graph_exec, kernel_nodes[0], &params1);

        // kernel2参数:i, d_matrix, d_array
        void* params_kernel2[] = {&i, &d_matrix, &d_array};
        cudaKernelNodeParams params2 = {
            .func = (void*)kernel2,
            .gridDim = dim3(1),
            .blockDim = dim3(512),
            .sharedMemBytes = 0,
            .kernelParams = params_kernel2,
            .extra = nullptr
        };
        cudaGraphExecKernelNodeSetParams(graph_exec, kernel_nodes[1], &params2);

        // kernel3参数:i, d_array
        void* params_kernel3[] = {&i, &d_array};
        cudaKernelNodeParams params3 = {
            .func = (void*)kernel3,
            .gridDim = dim3(1),
            .blockDim = dim3(512),
            .sharedMemBytes = 0,
            .kernelParams = params_kernel3,
            .extra = nullptr
        };
        cudaGraphExecKernelNodeSetParams(graph_exec, kernel_nodes[2], &params3);

        // 执行图(用stream1启动,图会自动调度两个流的操作)
        cudaGraphLaunch(graph_exec, stream1);
    }

    // 等待所有操作完成
    cudaStreamSynchronize(stream1);
    cudaStreamSynchronize(stream2);

    // 清理资源
    cudaStreamDestroy(stream1);
    cudaStreamDestroy(stream2);
    cudaEventDestroy(event1);
    cudaEventDestroy(event2);
    cudaGraphDestroy(graph);
    cudaGraphExecDestroy(graph_exec);
}

关键细节说明

  • 全局捕获模式:cudaStreamCaptureModeGlobal允许捕获多个流的操作,并自动将流间的依赖(通过cudaStreamWaitEvent和cudaEventRecord)转换成图节点间的依赖关系,无需手动处理。
  • 动态参数更新:每次迭代更新参数数组的内容,再调用cudaGraphExecKernelNodeSetParams更新对应kernel节点的参数,确保每次图执行时使用最新的i、矩阵和数组指针。
  • 节点收集:通过cudaGraphGetNodes遍历图中的所有节点,筛选出kernel节点,后续更新参数时直接操作这些节点即可。

内容的提问来源于stack exchange,提问作者Photos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 03:14:56