aclDumpOpTensors
Function
Dumps the input and output tensor data of operators during model execution. This helps locate and analyze operator input and output data issues.
Prototype
aclnnStatus aclDumpOpTensors(const char *opType, const char *opName, aclTensor **tensors, size_t inputTensorNum, size_t outputTensorNum, aclrtStream stream)
Parameters
|
Parameter |
Input/Output |
Description |
|---|---|---|
|
opType |
Input |
A string indicating the operator type, for example, Add. |
|
opName |
Input |
A string indicating the operator name, for example, add_custom. |
|
tensors |
Input |
1D tensor, indicating the pointers to the input/output tensors to dump. Note the tensor sequence. Input tensors must precede output tensors. |
|
inputTensorNum |
Input |
Number of input tensors to dump. |
|
outputTensorNum |
Input |
Number of output tensors to dump. |
|
stream |
Input |
Stream for executing the task. |
Returns
0 on success; otherwise, failure. For details about the return codes, see Common APIs and Return Codes.
Constraints
This API requires operator dump enabled. You can enable the dump function by calling aclInit or the aclmdlInitDump, aclmdlSetDump, and aclmdlFinalizeDump APIs. For details about the APIs, see aclInit and Dump Configuration in Application Development (C&C++).
Call Example
The following sample code is for reference only. Do not copy and run it.
- Enable operator dump by calling the aclInit API. See the following code snippet:
1 2 3 4 5
// AscendCL Init aclInit("./acl.json"); aclrtSetDevice(0); aclrtStream stream = nullptr; aclrtCreateStream(&stream);
The following is an example of the acl.json file. For details, see the model dump configuration and single-operator dump configuration examples in the aclInit API reference.1 2 3 4 5 6 7 8
{ "dump": { "dump_path": "./", "dump_list": [], "dump_mode": "all", "dump_data": "tensor" } }
- Refer to the following key pseudocode for calling this API (using the torch operator as an example):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
#include <torch/extension.h> #include "torch_npu/csrc/core/npu/NPUStream.h" #include "torch_npu/csrc/core/npu/NPUFunctions.h" #include "torch_npu/csrc/framework/OpCommand.h" #include "torch_npu/csrc/framework/interface/AclOpCompileInterface.h" #include "torch_npu/csrc/core/npu/register/OptionsManager.h" #include "torch_npu/csrc/aten/NPUNativeFunctions.h" #include "torch_npu/csrc/flopcount/FlopCount.h" #include "torch_npu/csrc/flopcount/FlopCounter.h" #include "torch_npu/csrc/core/npu/NpuVariables.h" #include "kernel_operator.h" #include <acl/acl_base.h> #include <aclnn/acl_meta.h> constexpr int32_t BUFFER_NUM = 2; constexpr int64_t MAX_DIM_NUM = 5; constexpr int64_t NCL_DIM_NUM = 3; constexpr int64_t NCHW_DIM_NUM = 4; constexpr int64_t NCDHW_DIM_NUM = 5; // Generate the 1D tensor of the input/output tensor object pointers of the operator to be dumped. #define INIT_ACL_TENSOR_ARRAY(tensors, ...) aclTensor* tensors[] = {__VA_ARGS__} // Convert the at::Tensor object into an aclTensor object. This function simplifies the processing. Configure the parameters based on the actual operator. aclTensor *ConvertTensor(const at::Tensor &at_tensor) { aclDataType acl_data_type = ACL_FLOAT16; c10::SmallVector<int64_t, MAX_DIM_NUM> storageDims; const auto dimNum = at_tensor.sizes().size(); aclFormat format = ACL_FORMAT_ND; switch (dimNum) { case NCL_DIM_NUM: format = ACL_FORMAT_NCL; break; case NCHW_DIM_NUM: format = ACL_FORMAT_NCHW; break; case NCDHW_DIM_NUM: format = ACL_FORMAT_NCDHW; break; default: format = ACL_FORMAT_ND; } // if acl_data_type is ACL_STRING, storageDims is empty. if (acl_data_type != ACL_STRING) { storageDims.push_back(at_tensor.storage().nbytes() / at_tensor.itemsize()); } auto acl_tensor = aclCreateTensor(at_tensor.sizes().data(), at_tensor.sizes().size(), acl_data_type, at_tensor.strides().data(), at_tensor.storage_offset(), format, storageDims.data(), storageDims.size(), const_cast<void *>(at_tensor.storage().data())); return acl_tensor; } // Custom operator implementation. Configure the parameters based on the actual operator. class KernelAdd { public: __aicore__ inline KernelAdd() {} __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR z, uint32_t totalLength) { this->blockLength = totalLength / AscendC::GetBlockNum(); this->tileNum = 8; this->tileLength = this->blockLength / this->tileNum / BUFFER_NUM; xGm.SetGlobalBuffer((__gm__ half *)x + this->blockLength * AscendC::GetBlockIdx(), this->blockLength); yGm.SetGlobalBuffer((__gm__ half *)y + this->blockLength * AscendC::GetBlockIdx(), this->blockLength); zGm.SetGlobalBuffer((__gm__ half *)z + this->blockLength * AscendC::GetBlockIdx(), this->blockLength); pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(half)); pipe.InitBuffer(inQueueY, BUFFER_NUM, this->tileLength * sizeof(half)); pipe.InitBuffer(outQueueZ, BUFFER_NUM, this->tileLength * sizeof(half)); } __aicore__ inline void Process() { int32_t loopCount = this->tileNum * BUFFER_NUM; for (int32_t i = 0; i < loopCount; i++) { CopyIn(i); Compute(i); CopyOut(i); } } private: __aicore__ inline void CopyIn(int32_t progress) { AscendC::LocalTensor<half> xLocal = inQueueX.AllocTensor<half>(); AscendC::LocalTensor<half> yLocal = inQueueY.AllocTensor<half>(); AscendC::DataCopy(xLocal, xGm[progress * this->tileLength], this->tileLength); AscendC::DataCopy(yLocal, yGm[progress * this->tileLength], this->tileLength); inQueueX.EnQue(xLocal); inQueueY.EnQue(yLocal); } __aicore__ inline void Compute(int32_t progress) { AscendC::LocalTensor<half> xLocal = inQueueX.DeQue<half>(); AscendC::LocalTensor<half> yLocal = inQueueY.DeQue<half>(); AscendC::LocalTensor<half> zLocal = outQueueZ.AllocTensor<half>(); AscendC::Add(zLocal, xLocal, yLocal, this->tileLength); outQueueZ.EnQue<half>(zLocal); inQueueX.FreeTensor(xLocal); inQueueY.FreeTensor(yLocal); } __aicore__ inline void CopyOut(int32_t progress) { AscendC::LocalTensor<half> zLocal = outQueueZ.DeQue<half>(); AscendC::DataCopy(zGm[progress * this->tileLength], zLocal, this->tileLength); outQueueZ.FreeTensor(zLocal); } private: AscendC::TPipe pipe; AscendC::TQue<AscendC::TPosition::VECIN, BUFFER_NUM> inQueueX, inQueueY; AscendC::TQue<AscendC::TPosition::VECOUT, BUFFER_NUM> outQueueZ; AscendC::GlobalTensor<half> xGm; AscendC::GlobalTensor<half> yGm; AscendC::GlobalTensor<half> zGm; uint32_t blockLength; uint32_t tileNum; uint32_t tileLength; }; __global__ __vector__ void add_custom(GM_ADDR x, GM_ADDR y, GM_ADDR z, uint32_t totalLength) { KernelAdd op; op.Init(x, y, z, totalLength); op.Process(); } namespace ascendc_ops { at::Tensor ascendc_add(const at::Tensor& x, const at::Tensor& y) { auto aclStream = c10_npu::getCurrentNPUStream().stream(false); at::Tensor z = at::empty_like(x); uint32_t numBlocks = 8; uint32_t totalLength = 1; for (uint32_t size : x.sizes()) { totalLength *= size; } add_custom<<<numBlocks, nullptr, aclStream>>>((uint8_t*)(x.mutable_data_ptr()), (uint8_t*)(y.mutable_data_ptr()), (uint8_t*)(z.mutable_data_ptr()), totalLength); // Dump the input and output tensor data of the operator. INIT_ACL_TENSOR_ARRAY(tensors, ConvertTensor(x), ConvertTensor(y), ConvertTensor(z)); aclDumpOpTensors("Add", "add_custom", tensors, 2, 1, aclStream); // Destroy the aclTensor object. for (size_t i = 0; i < 3; i++) { aclDestroyTensor(tensors[i]); } return z; } } // namespace ascendc_ops TORCH_LIBRARY(ascendc_ops, m) { m.def("ascendc_add(Tensor x, Tensor y) -> Tensor"); } TORCH_LIBRARY_IMPL(ascendc_ops, PrivateUse1, m) { m.impl("ascendc_add", TORCH_FN(ascendc_ops::ascendc_add)); }