1 /**
2 * Copyright 2021 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/litert/delegate/npu/npu_executor.h"
18 #include <unordered_map>
19 #include <set>
20 #include "include/errorcode.h"
21 #include "src/litert/delegate/npu/npu_manager.h"
22 #include "src/common/log_adapter.h"
23
24 namespace mindspore::lite {
~NPUExecutor()25 NPUExecutor::~NPUExecutor() {
26 client_.reset();
27 for (auto t : npu_input_tensors_) {
28 t.reset();
29 }
30 npu_input_tensors_.clear();
31 for (auto t : npu_output_tensors_) {
32 t.reset();
33 }
34 npu_output_tensors_.clear();
35 }
36
Prepare()37 int NPUExecutor::Prepare() {
38 MS_ASSERT(npu_manager_ != nullptr);
39 this->client_ = npu_manager_->GetClient(model_name_);
40 if (this->client_ == nullptr) {
41 MS_LOG(ERROR) << "client is nullptr.";
42 return RET_ERROR;
43 }
44 if (GetIOTensorVec() != RET_OK) {
45 MS_LOG(ERROR) << "NPUExecutor GetIOTensorVec failed.";
46 return RET_ERROR;
47 }
48 return RET_OK;
49 }
50
GetNpuTensorShape(int dim,std::shared_ptr<hiai::AiTensor> npu_tensor)51 std::vector<int64_t> GetNpuTensorShape(int dim, std::shared_ptr<hiai::AiTensor> npu_tensor) {
52 std::vector<int64_t> npu_shape;
53 if (dim > 0) {
54 npu_shape.push_back(npu_tensor->GetTensorDimension().GetNumber());
55 }
56 if (dim > 1) {
57 npu_shape.push_back(npu_tensor->GetTensorDimension().GetChannel());
58 }
59 if (dim > 2) {
60 npu_shape.push_back(npu_tensor->GetTensorDimension().GetHeight());
61 }
62 if (dim > 3) {
63 npu_shape.push_back(npu_tensor->GetTensorDimension().GetWidth());
64 }
65 return npu_shape;
66 }
67
IsSameShapeTensor(mindspore::MSTensor tensor,const std::shared_ptr<hiai::AiTensor> & npu_tensor)68 bool IsSameShapeTensor(mindspore::MSTensor tensor, const std::shared_ptr<hiai::AiTensor> &npu_tensor) {
69 if (tensor.Shape().size() > NPU_SHAPE_SIZE) {
70 MS_LOG(ERROR) << "Npu does not support output tensor dims greater than 4";
71 return false;
72 }
73 return GetNpuTensorShape(tensor.Shape().size(), npu_tensor) == tensor.Shape();
74 }
75
Run(const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & valid_out_tensors,const std::vector<mindspore::MSTensor> & all_out_tensors,const std::vector<NPUOp * > & out_ops)76 int NPUExecutor::Run(const std::vector<mindspore::MSTensor> &in_tensors,
77 const std::vector<mindspore::MSTensor> &valid_out_tensors,
78 const std::vector<mindspore::MSTensor> &all_out_tensors, const std::vector<NPUOp *> &out_ops) {
79 hiai::AiContext context;
80 for (size_t i = 0; i < npu_input_tensors_.size(); ++i) {
81 MS_CHECK_TRUE_RET(i < input_relationship_.size() && input_relationship_.at(i) < in_tensors.size(), RET_ERROR);
82 auto inx = input_relationship_.at(i);
83 auto data = in_tensors[inx].Data();
84 if (data == nullptr) {
85 MS_LOG(ERROR) << "For " << model_name_ << ", the input tensor " << in_tensors[inx].Name() << " data is nullptr";
86 return RET_ERROR;
87 }
88 memcpy(npu_input_tensors_[i]->GetBuffer(), data.get(), in_tensors[inx].DataSize());
89 }
90 context.AddPara("model_name", model_name_);
91 if (this->client_ == nullptr) {
92 MS_LOG(ERROR) << "NPU client is nullptr";
93 return RET_ERROR;
94 }
95 int stamp;
96 int ret = this->client_->Process(context, this->npu_input_tensors_, this->npu_output_tensors_, 1000, stamp);
97 if (ret != hiai::AI_SUCCESS) {
98 MS_LOG(ERROR) << "NPU Process failed. code is " << ret;
99 return RET_ERROR;
100 }
101
102 // if the multi-output op is the graph out op, all of its output tensor will be treat as graph output for om model.
103 std::set<schema::PrimitiveType> multi_output_list = {schema::PrimitiveType_Split};
104 bool has_multi_output_op = false;
105 for (auto out_op : out_ops) {
106 if (std::find(multi_output_list.begin(), multi_output_list.end(), out_op->type()) != multi_output_list.end()) {
107 has_multi_output_op = true;
108 break;
109 }
110 }
111
112 if (npu_output_tensors_.size() != all_out_tensors.size() ||
113 (!has_multi_output_op && npu_output_tensors_.size() != valid_out_tensors.size())) {
114 MS_LOG(ERROR) << "The output count (" << npu_output_tensors_.size() << ") is not equal to ms tensor ("
115 << all_out_tensors.size() << ").";
116 return RET_ERROR;
117 }
118 for (size_t i = 0; i < npu_output_tensors_.size(); ++i) {
119 mindspore::MSTensor out_tensor = all_out_tensors[i];
120 if (std::find(valid_out_tensors.begin(), valid_out_tensors.end(), out_tensor) != valid_out_tensors.end()) {
121 auto data = out_tensor.MutableData();
122 if (data == nullptr) {
123 MS_LOG(ERROR) << "For " << model_name_ << ", the output tensor " << out_tensor.Name() << " data is nullptr";
124 return RET_ERROR;
125 }
126 memcpy(data, npu_output_tensors_[i]->GetBuffer(), npu_output_tensors_[i]->GetSize());
127 }
128 }
129 return RET_OK;
130 }
131
GetIOTensorVec()132 int NPUExecutor::GetIOTensorVec() {
133 std::vector<hiai::TensorDimension> input_dimension;
134 std::vector<hiai::TensorDimension> output_dimension;
135 input_dimension.clear();
136 output_dimension.clear();
137 if (this->client_ == nullptr) {
138 MS_LOG(ERROR) << "client is nullptr.";
139 return RET_ERROR;
140 }
141 auto ret = this->client_->GetModelIOTensorDim(model_name_, input_dimension, output_dimension);
142 if (ret != hiai::AI_SUCCESS) {
143 MS_LOG(ERROR) << "Get model input and output tensor dims failed." << ret;
144 return RET_ERROR;
145 }
146 ret = UpdateInputTensorVec(input_dimension);
147 if (ret != RET_OK) {
148 MS_LOG(ERROR) << "Update input tensor vector failed. " << ret;
149 return RET_ERROR;
150 }
151 ret = UpdateOutputTensorVec(output_dimension);
152 if (ret != RET_OK) {
153 MS_LOG(ERROR) << "Update output tensor vector failed. " << ret;
154 return RET_ERROR;
155 }
156 return RET_OK;
157 }
158
UpdateInputTensorVec(const std::vector<hiai::TensorDimension> & input_dimension)159 int NPUExecutor::UpdateInputTensorVec(const std::vector<hiai::TensorDimension> &input_dimension) {
160 if (input_dimension.empty()) {
161 MS_LOG(ERROR) << "npu input tensor dimension is empty.";
162 return RET_ERROR;
163 }
164 npu_input_tensors_.resize(input_dimension.size());
165 npu_input_tensors_.clear();
166 for (const auto &inDim : input_dimension) {
167 std::shared_ptr<hiai::AiTensor> input = std::make_shared<hiai::AiTensor>();
168 if (input->Init(&inDim) != hiai::AI_SUCCESS) {
169 MS_LOG(ERROR) << "Input AiTensor init failed.";
170 return RET_ERROR;
171 }
172 npu_input_tensors_.push_back(input);
173 }
174 if (npu_input_tensors_.empty()) {
175 MS_LOG(ERROR) << "NPU input tensor is empty.";
176 return RET_ERROR;
177 }
178 return RET_OK;
179 }
180
UpdateOutputTensorVec(const std::vector<hiai::TensorDimension> & output_dimension)181 int NPUExecutor::UpdateOutputTensorVec(const std::vector<hiai::TensorDimension> &output_dimension) {
182 if (output_dimension.empty()) {
183 MS_LOG(ERROR) << "output_dimension_ is empty.";
184 return RET_ERROR;
185 }
186 npu_output_tensors_.resize(output_dimension.size());
187 npu_output_tensors_.clear();
188 for (const auto &outDim : output_dimension) {
189 std::shared_ptr<hiai::AiTensor> output = std::make_shared<hiai::AiTensor>();
190 int ret = output->Init(&outDim);
191 if (ret != hiai::AI_SUCCESS) {
192 return RET_ERROR;
193 }
194 npu_output_tensors_.push_back(output);
195 }
196 if (npu_output_tensors_.empty()) {
197 MS_LOG(ERROR) << "NPU output tensor is empty.";
198 return RET_ERROR;
199 }
200 return RET_OK;
201 }
202 } // namespace mindspore::lite
203