使用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
相关产品推荐
相关产品推荐

