对于支持动态Shape的算子:
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// ......
const char *opType;
int numInputs;
aclTensorDesc *inputDesc[2];
aclDataBuffer *inputs[2];
int numOutputs;
aclTensorDesc *outputDesc[1];
aclopAttr *attr;
aclError ret = aclopInferShape(opType, numInputs, inputDesc, inputs,numOutputs, outputDesc, attr);
std::vector<std::vector<int64_t>> tensorDims; // inferShape之后的输出tensor的Shape
// 循环算子的每一个输出,推导或预估Shape值:
for (int index = 0; index < numOutputs; ++index) {
std::vector<int64_t> dimSize; // 表示执行算子时,输出的shape
size_t dimNums = aclGetTensorDescNumDims(outputDesc[index]);
// 表示动态Shape场景下维度个数未知,该场景预留
if (dimNums == ACL_UNKNOWN_RANK) {
// 由用户预估最大Shape值max shape
dimSize.push_back(max_shape);
} else {
for (size_t i = 0; i < dimNums; ++i) {
int64_t dim;
ret = aclGetTensorDescDimV2(outputDesc[index], i, &dim);
// 表示动态Shape场景下维度值是动态的
if(dim == -1) {
int64_t dimRange[2];
// 获取Shape范围,使用该范围中的Shape最大值来构造输出tensorDesc,作为aclopExecuteV2的输入
ret = aclGetTensorDescDimRange(outputDesc[index], i, 2, dimRange);
dim = dimRange[1];
}
dimSize.push_back(dim);
}
}
tensorDims.push_back(dimSize);
}
// 构造算子输入tensorDesc和输入tensor,作为aclOpExecuteV2的输入
aclTensorDesc *inputDescNew[2];
aclDataBuffer *inputsNew[2];
aclDataBuffer *outputsNew[1];
// 以上给出了执行算子时输出的shape, 根据tensorDims中的dims构造输出tensorDesc(即outputDescNew参数值), 用于调用aclopExecuteV2
ret = aclopExecuteV2(opType, numInputs, inputDescNew, inputsNew, numOutputs, outputDescNew, outputsNew, attr, stream);
// 针对上面用户预估Shape值以及使用Shape范围中的最大Shape的场景,在算子执行结束后,需增加下面的调用,获取准确的shape:
// for 循环每一个输出的tensorDesc
std::vector<std::vector<int64_t>> outTensorDims; // 准确的输出tensorShape
for (int index = 0; index < numOutputs; ++index) {
std::vector<int64_t> dimSize;
int dimNums = aclGetTensorDescNumDims(outputDescNew[index]);
for (int i = 0; i < dimNums; i++){
int64_t dim;
ret = aclGetTensorDescDimV2(outputDescNew[index], i, &dim);
dimSize.push_back(dim);
}
outTensorDims.push_back(dimSize);
}
// ......
通过在线视频课程学习该功能,请参见CANN应用开发高级。