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

使用cuDNN Graph API创建engineConfig时遇CUDNN_STATUS_NOT_SUPPORTED错误

cuDNN Graph API创建EngineConfig时触发CUDNN_STATUS_NOT_SUPPORTED错误排查

问题描述

基于cuDNN Graph API自定义实现,未使用cudnn_frontend库,直接调用cuDNN后端API,在创建engineConfig时触发CUDNN_STATUS_NOT_SUPPORTED错误。

问题代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>

#include <cudnn.h>

void assertDescriptorIsNull(cudnnBackendDescriptor_t desc) {
  if (desc == NULL) {
    fprintf(stderr, "Error: descriptor is not NULL\n");
    exit(-1);
  }
}

cudnnBackendDescriptor_t tensorDescriptorCreate(
  int64_t numDim, 
  int64_t *dim, 
  int64_t *stride, 
  int64_t byteAlignment, 
  cudnnDataType_t dataType, 
  std::string name
) {
  const char *name_ptr = name.c_str();
  cudnnBackendDescriptor_t tensorDesc;
  CHECK_CUDNN(cudnnBackendCreateDescriptor(CUDNN_BACKEND_TENSOR_DESCRIPTOR, &tensorDesc));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendSetAttribute(tensorDesc, 
                           CUDNN_ATTR_TENSOR_DATA_TYPE, CUDNN_TYPE_DATA_TYPE, 1, &dataType));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendSetAttribute(tensorDesc, 
                          CUDNN_ATTR_TENSOR_DIMENSIONS, CUDNN_TYPE_INT64, numDim, dim));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendSetAttribute(tensorDesc, 
                           CUDNN_ATTR_TENSOR_STRIDES, CUDNN_TYPE_INT64, numDim, stride));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendSetAttribute(tensorDesc, 
                           CUDNN_ATTR_TENSOR_BYTE_ALIGNMENT, CUDNN_TYPE_INT64, 1, &byteAlignment));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendSetAttribute(tensorDesc, 
                           CUDNN_ATTR_TENSOR_UNIQUE_ID, CUDNN_TYPE_INT64, 1, name_ptr));
  assertDescriptorIsNull(tensorDesc);

  CHECK_CUDNN(cudnnBackendFinalize(tensorDesc));
  assertDescriptorIsNull(tensorDesc);

  return tensorDesc;
}

cudnnBackendDescriptor_t init_graph(cudnnHandle_t cudnn) {
  cudnnBackendDescriptor_t graph;
  CHECK_CUDNN(cudnnBackendCreateDescriptor(CUDNN_BACKEND_OPERATIONGRAPH_DESCRIPTOR, &graph));
  CHECK_CUDNN(cudnnBackendSetAttribute(graph, CUDNN_ATTR_OPERATIONGRAPH_HANDLE, CUDNN_TYPE_HANDLE, 1, &cudnn));
  return graph;
}

void finalize_graph(cudnnBackendDescriptor_t graph) {
  CHECK_CUDNN(cudnnBackendFinalize(graph));
}

cudnnBackendDescriptor_t create_engine_by_graph(cudnnBackendDescriptor_t graph) {
  cudnnBackendDescriptor_t engine;
  CHECK_CUDNN(cudnnBackendCreateDescriptor(CUDNN_BACKEND_ENGINE_DESCRIPTOR, &engine));
  CHECK_CUDNN(cudnnBackendSetAttribute(engine, CUDNN_ATTR_ENGINE_OPERATION_GRAPH,
                          CUDNN_TYPE_BACKEND_DESCRIPTOR, 1, &graph));
  int64_t gidx = 0;
  CHECK_CUDNN(cudnnBackendSetAttribute(engine, CUDNN_ATTR_ENGINE_GLOBAL_INDEX,
                          CUDNN_TYPE_INT64, 1, &gidx));
  CHECK_CUDNN(cudnnBackendFinalize(engine));
  return engine;
}

struct EngineConfig {
  cudnnBackendDescriptor_t engcfg;
  int64_t workspaceSize;
};

struct EngineConfig engineConfigDescriptorCreate(cudnnBackendDescriptor_t engine) {
  cudnnBackendDescriptor_t engcfg;
  CHECK_CUDNN(cudnnBackendCreateDescriptor(CUDNN_BACKEND_ENGINECFG_DESCRIPTOR, &engcfg));
  CHECK_CUDNN(cudnnBackendSetAttribute(engcfg, CUDNN_ATTR_ENGINECFG_ENGINE,
                          CUDNN_TYPE_BACKEND_DESCRIPTOR, 1, &engine));
  /// here
  CHECK_CUDNN(cudnnBackendFinalize(engcfg)); /// error here!!!!
  /// here

  int64_t workspaceSize;
  CHECK_CUDNN(cudnnBackendGetAttribute(engcfg, CUDNN_ATTR_ENGINECFG_WORKSPACE_SIZE,
                          CUDNN_TYPE_INT64, 1, NULL, &workspaceSize));
  struct EngineConfig config = {engcfg, workspaceSize};
  return config;
}

class NormConfig {
private:
  cudnnHandle_t cudnn;

  cudnnBackendDescriptor_t norm_desc;
  cudnnBackendDescriptor_t mode;
  cudnnBackendDescriptor_t phase;
  cudnnBackendDescriptor_t x_desc;
  cudnnBackendDescriptor_t y_desc;
  cudnnBackendDescriptor_t mean_desc;
  cudnnBackendDescriptor_t inv_var_desc;
  cudnnBackendDescriptor_t scale_desc;
  cudnnBackendDescriptor_t bias_desc;
  cudnnBackendDescriptor_t epsilon_desc;
  cudnnBackendDescriptor_t input_running_mean_desc;
  cudnnBackendDescriptor_t input_running_var_desc;
  cudnnBackendDescriptor_t output_running_mean_desc;
  cudnnBackendDescriptor_t output_running_var_desc;

  cudnnBackendDescriptor_t op_graph;

  void setAttribute(cudnnBackendDescriptor_t desc, cudnnBackendAttributeName_t attr, cudnnBackendAttributeType_t type, int64_t num, void *value) {
    CHECK_CUDNN(cudnnBackendSetAttribute(desc, attr, type, num, value));
  }

  void setTensorAttribute(cudnnBackendDescriptor_t desc, cudnnBackendAttributeName_t attr, cudnnBackendDescriptor_t tensor_desc) {
    this->setAttribute(desc, attr, CUDNN_TYPE_BACKEND_DESCRIPTOR, 1, &tensor_desc);
  }

public:
  NormConfig(cudnnHandle_t cudnn_, cudnnBackendDescriptor_t graph) : cudnn(cudnn_), op_graph(graph) {}

  void CreateNormDesc(
    int64_t batch_size,
    int64_t channels,
    int64_t height,
    int64_t width,
    cudnnBackendNormMode_t mode,
    cudnnBackendNormFwdPhase_t phase
  ) {
    CHECK_CUDNN(cudnnBackendCreateDescriptor(CUDNN_BACKEND_OPERATION_NORM_FORWARD_DESCRIPTOR, &this->norm_desc));
    setAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_MODE, CUDNN_TYPE_NORM_MODE, 1, &mode);
    setAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_PHASE, CUDNN_TYPE_NORM_FWD_PHASE, 1, &phase);

    int64_t dims[4] = {batch_size, channels, height, width};
    int64_t strides[4] = {channels * height * width, height * width, width, 1};
    int64_t scalar[4] = {1, 1, 1, 1};
    int64_t dim2d[4] = {1, channels, 1, 1};
    int64_t dim2d_stride[4] = {channels, 1, channels, channels};

    this->x_desc = tensorDescriptorCreate(4, dims, strides, 4, CUDNN_DATA_FLOAT, std::string("x"));
    this->y_desc = tensorDescriptorCreate(4, dims, strides, 4, CUDNN_DATA_FLOAT, std::string("y"));
    this->mean_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("mean"));
    this->inv_var_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("inv_var"));
    this->scale_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("scale"));
    this->bias_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("bias"));
    this->epsilon_desc = tensorDescriptorCreate(4, scalar, scalar, 4, CUDNN_DATA_FLOAT, std::string("epsilon"));
    this->input_running_mean_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("input_running_mean"));
    this->input_running_var_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("input_running_var"));
    this->output_running_mean_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("output_running_mean"));
    this->output_running_var_desc = tensorDescriptorCreate(4, dim2d, dim2d_stride, 4, CUDNN_DATA_FLOAT, std::string("output_running_var"));

    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_XDESC, this->x_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_MEAN_DESC, this->mean_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_INV_VARIANCE_DESC, this->inv_var_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_SCALE_DESC, this->scale_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_BIAS_DESC, this->bias_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_EPSILON_DESC, this->epsilon_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_INPUT_RUNNING_MEAN_DESC, this->input_running_mean_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_INPUT_RUNNING_VAR_DESC, this->input_running_var_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_OUTPUT_RUNNING_MEAN_DESC, this->output_running_mean_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_OUTPUT_RUNNING_VAR_DESC, this->output_running_var_desc);
    setTensorAttribute(this->norm_desc, CUDNN_ATTR_OPERATION_NORM_FWD_YDESC, this->y_desc);

    CHECK_CUDNN(cudnnBackendFinalize(this->norm_desc));
  };

  void register_graph() {
    CHECK_CUDNN(cudnnBackendSetAttribute(this->op_graph, CUDNN_ATTR_OPERATIONGRAPH_OPS, CUDNN_TYPE_BACKEND_DESCRIPTOR, 1, &this->norm_desc));
  }
};

int main() {
  cudnnHandle_t cudnn;
  CHECK_CUDNN(cudnnCreate(&cudnn));
  cudnnBackendDescriptor_t graph = init_graph(cudnn);

  NormConfig norm_config = NormConfig(cudnn, graph);
  norm_config.CreateNormDesc(4, 32, 16, 16, CUDNN_BATCH_NORM, CUDNN_NORM_FWD_TRAINING);
  norm_config.register_graph();

  finalize_graph(graph);

  cudnnBackendDescriptor_t engine = create_engine_by_graph(graph);

  struct EngineConfig config = engineConfigDescriptorCreate(engine);

  return 0;
}

错误信息

CUDNN Error: /path to file :87, reason: CUDNN_STATUS_NOT_SUPPORTED

环境配置

  • CUDA版本:12.4
  • cuDNN版本:9.0.0
  • GPU:NVIDIA GeForce RTX 2080Ti
  • 操作系统:Ubuntu 22.04
  • 编译器:GCC 9.3.0
  • 构建系统:CMake 3.16.3

问题解答

1. 使用cuDNN Graph API创建engineConfig的正确步骤

创建EngineConfig的标准流程如下:

  • 步骤1:创建EngineConfig描述符
    调用cudnnBackendCreateDescriptor(CUDNN_BACKEND_ENGINECFG_DESCRIPTOR, &engcfg)初始化描述符。
  • 步骤2:绑定关联的Engine
    通过cudnnBackendSetAttribute设置CUDNN_ATTR_ENGINECFG_ENGINE属性,关联已初始化并finalize的Engine描述符。
  • 步骤3:设置可选属性(如 workspace 限制)
    如果需要限制workspace大小,设置CUDNN_ATTR_ENGINECFG_MAX_WORKSPACE_SIZE属性;若无需限制,可跳过此步。
  • 步骤4:Finalize EngineConfig
    调用cudnnBackendFinalize(engcfg)完成描述符初始化。
  • 步骤5:查询所需资源(可选)
    通过cudnnBackendGetAttribute获取CUDNN_ATTR_ENGINECFG_WORKSPACE_SIZE等资源参数。

2. 导致该错误的常见问题及排查点

结合你的代码,重点排查以下几点:

  • Tensor描述符属性错误
    你的tensorDescriptorCreate函数中,设置CUDNN_ATTR_TENSOR_UNIQUE_ID时,错误使用了CUDNN_TYPE_INT64类型,而该属性实际要求是CUDNN_TYPE_STRING。类型不匹配会导致后续EngineConfig初始化失败。
  • BatchNorm操作的参数完整性
    在训练阶段的BatchNorm中,部分可选参数是否符合要求?比如你的epsilon用了4D tensor描述符,虽然合法,但需确认是否与cuDNN的要求兼容;另外,检查mean/inv_var等张量的stride设置是否合理(你的dim2d_stride设置为{channels,1,channels,channels},可能不符合连续张量的要求,建议改为{channels,1,1,1})。
  • Engine初始化的兼容性
    确认Engine创建时的CUDNN_ATTR_ENGINE_GLOBAL_INDEX设置是否合理,虽然设为0通常没问题,但部分场景下可能需要根据GPU设备调整;另外,检查Graph是否正确包含所有操作且已完成finalize。
  • 硬件与版本兼容性
    RTX 2080Ti属于Turing架构,确认cuDNN 9.0.0对该架构的Graph API支持是否完整,部分操作可能在旧架构上存在限制。

3. 是否可不依赖cudnn_frontend,直接通过后端API正确实现Graph API?

完全可以。cudnn_frontend只是cuDNN后端API的封装层,所有功能都可以通过直接调用后端API实现。但需要注意:

  • 必须严格遵循cuDNN后端API的属性类型、参数顺序要求,任何属性类型不匹配、必填参数缺失都会导致错误。
  • 需要自行管理描述符的生命周期,确保每个描述符在使用前完成finalize,使用后及时销毁。
  • 建议参考cuDNN官方文档中关于后端API的详细定义,尤其是每个描述符的必填属性、类型说明。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:05:54