1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "executor.h"
17
18 #include "securec.h"
19
20 #include "common/utils.h"
21 #include "common/scoped_trace.h"
22 #include "transform.h"
23
24
25 namespace OHOS {
26 namespace NeuralNetworkRuntime {
Executor(const Compilation * compilation)27 Executor::Executor(const Compilation* compilation)
28 : m_modelInputs(compilation->GetInputTensors()),
29 m_modelOutputs(compilation->GetOutputTensors()),
30 m_executionPlan(compilation->GetExecutionPlan()) {}
31
BuildInputTensor(uint32_t index,const OH_NN_Tensor & nnTensor,std::shared_ptr<NNTensor> inputTensor) const32 OH_NN_ReturnCode Executor::BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
33 std::shared_ptr<NNTensor> inputTensor) const
34 {
35 // Note: inputs have only shapes info.
36 if (index >= m_modelInputs.size()) {
37 LOGE("BuildInputTensor failed, input index is out of range.");
38 return OH_NN_INVALID_PARAMETER;
39 }
40
41 // Build a tensor from nnTensor.
42 auto ret = inputTensor->BuildFromOHNNTensor(nnTensor);
43 if (ret != OH_NN_SUCCESS) {
44 LOGE("BuildInputTensor failed, please check input nnTensor.");
45 return ret;
46 }
47
48 if (inputTensor->IsDynamicShape()) {
49 LOGE("BuildInputTensor failed, input nnTensor should has certain dimensions which cannot contain -1.");
50 return OH_NN_INVALID_PARAMETER;
51 }
52
53 inputTensor->SetFormat(m_modelInputs[index]->GetFormat());
54 if (!m_modelInputs[index]->CompareAttribute(*inputTensor)) {
55 LOGE("BuildInputTensor failed, input has different attributes from the one in the constructed model.");
56 return OH_NN_INVALID_PARAMETER;
57 }
58
59 inputTensor->SetName(m_modelInputs[index]->GetName());
60 return OH_NN_SUCCESS;
61 }
62
63
SetInputTensorWithCurrentBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * buffer,size_t dataLength,size_t curBufferLength)64 OH_NN_ReturnCode Executor::SetInputTensorWithCurrentBuffer(uint32_t index,
65 std::shared_ptr<NNTensor> inputTensor,
66 const void* buffer,
67 size_t dataLength,
68 size_t curBufferLength)
69 {
70 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
71 errno_t status = memcpy_s(curBuffer, dataLength, buffer, dataLength);
72 // Current buffer inside m_inputTensors is managed by executor, no need to release if memcpy failed.
73 if (status != EOK) {
74 LOGE("SetInputTensorWithCurrentBuffe failed, copy data from user buffer to device buffer failed. "
75 "Error code: %d.", status);
76 return OH_NN_MEMORY_ERROR;
77 }
78
79 // Set the new tensor with the buffer of current tensor
80 inputTensor->SetBuffer(curBuffer, curBufferLength);
81
82 // The memory is reused here. Thus, current tensor's buffer must set to nullptr, in case the memory is released
83 // twice.
84 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
85
86 // Set to the new tensor, and release current one.
87 m_inputTensors[index].tensor = inputTensor;
88 return OH_NN_SUCCESS;
89 }
90
91
SetInputTensorWithNewBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * inputBuffer,size_t length,bool isInnerMem)92 void Executor::SetInputTensorWithNewBuffer(uint32_t index,
93 std::shared_ptr<NNTensor> inputTensor,
94 const void* inputBuffer,
95 size_t length,
96 bool isInnerMem)
97 {
98 // Release the memory inside the tensor first, if it is allocated by Executor during SetInput().
99 if (m_inputTensors.find(index) != m_inputTensors.end()) {
100 if (m_inputTensors[index].isInnerMem) {
101 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
102 std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
103 inputDevice->ReleaseBuffer(curBuffer);
104 }
105 // Set current tensor's buffer to nullptr in case the NNTensor release the driver memory in destruction.
106 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
107 }
108
109 // Set new input tensor data buffer
110 inputTensor->SetBuffer(inputBuffer, length);
111
112 // Create or update the input tensor
113 ExeTensor exeTensor{inputTensor, nullptr, 0, isInnerMem};
114 m_inputTensors[index] = exeTensor;
115 }
116
117
CheckInputDimRanges(uint32_t index,const OH_NN_Tensor & nnTensor) const118 OH_NN_ReturnCode Executor::CheckInputDimRanges(uint32_t index, const OH_NN_Tensor& nnTensor) const
119 {
120 std::vector<std::vector<uint32_t>> minInputDims;
121 std::vector<std::vector<uint32_t>> maxInputDims;
122 auto ret = m_executionPlan->GetInputDimRanges(minInputDims, maxInputDims);
123 if (ret != OH_NN_SUCCESS) {
124 LOGE("Get the dimension ranges of input %u failed. ErrorCode=%d", index, ret);
125 return ret;
126 }
127
128 if (index >= minInputDims.size()) {
129 LOGE("index is %u, which exceeds the size of minInputDims:%zu.", index, minInputDims.size());
130 return OH_NN_INVALID_PARAMETER;
131 }
132
133 if (index >= maxInputDims.size()) {
134 LOGE("index is %u, which exceeds the size of maxInputDims:%zu.", index, maxInputDims.size());
135 return OH_NN_INVALID_PARAMETER;
136 }
137
138 const std::vector<uint32_t>& minSingleInputDims = minInputDims[index];
139 const std::vector<uint32_t>& maxSingleInputDims = maxInputDims[index];
140
141 std::vector<int32_t> tensorShape = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
142 size_t tensorShapeSize = tensorShape.size();
143 if (minSingleInputDims.size() != tensorShapeSize || maxSingleInputDims.size() != tensorShapeSize) {
144 LOGE("Size of minSingleInputDims, maxSingleInputDims and tensorShape of input %u are not equal.", index);
145 return OH_NN_INVALID_PARAMETER;
146 }
147
148 for (size_t j = 0; j < tensorShapeSize; ++j) {
149 // Dimensions cannot be negative
150 if (tensorShape[j] < 0) {
151 LOGE("Dimension %zu of input %u is %d.", j, index, tensorShape[j]);
152 return OH_NN_INVALID_PARAMETER;
153 }
154 uint32_t dim = static_cast<uint32_t>(tensorShape[j]);
155 if (dim < minSingleInputDims[j] || dim > maxSingleInputDims[j]) {
156 LOGE("Dimension %zu of input %u is %u, which is out of range [%u, %u]",
157 j, index, dim, minSingleInputDims[j], maxSingleInputDims[j]);
158 return OH_NN_INVALID_PARAMETER;
159 }
160 }
161
162 return OH_NN_SUCCESS;
163 }
164
165
SetInput(uint32_t index,const OH_NN_Tensor & nnTensor,const void * buffer,size_t length)166 OH_NN_ReturnCode Executor::SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length)
167 {
168 auto nnRet = CheckInputDimRanges(index, nnTensor);
169 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
170 LOGI("Skip input dimension bounds check.");
171 } else if (nnRet != OH_NN_SUCCESS) {
172 LOGE("SetInput failed, Check the range of the %uth input dimension ranges failed.", index);
173 return nnRet;
174 }
175
176 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
177 if (inputTensor == nullptr) {
178 LOGE("SetInput failed, error happened when creating NNTensor.");
179 return OH_NN_MEMORY_ERROR;
180 }
181
182 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
183 if (ret != OH_NN_SUCCESS) {
184 LOGE("SetInput failed, please check input index or nnTensor.");
185 return ret;
186 }
187
188 // dataLength will be larger than 0 after BuildInputTensor()
189 size_t dataLength = inputTensor->GetDataLength();
190 if (length == 0 || length < dataLength) {
191 LOGE("SetInput failed, the given buffer length is too small to store the input nnTensor data.");
192 return OH_NN_INVALID_PARAMETER;
193 }
194
195 // Get length of current buffer if it is allocate by SetInput() before.
196 size_t curBufferLength = 0;
197 if ((m_inputTensors.find(index) != m_inputTensors.end()) && (m_inputTensors[index].isInnerMem)) {
198 curBufferLength = m_inputTensors[index].tensor->GetBufferLength();
199 }
200
201 // (dataLength <= curBufferLength) returns true if and only if current buffer is allocated by SetInput() before
202 // and is larger than user buffer.
203 if (dataLength <= curBufferLength) {
204 ret = SetInputTensorWithCurrentBuffer(index, inputTensor, buffer, dataLength, curBufferLength);
205 if (ret != OH_NN_SUCCESS) {
206 LOGE("SetInput failed, error happened when setting input with current buffer.");
207 return ret;
208 }
209 m_isRun = false;
210 return OH_NN_SUCCESS;
211 }
212
213 /**
214 * Buffer needs to allocated or reallocated if:
215 *
216 * - Current buffer is not enough.
217 * - SetInput() has not been called for the input before.
218 * - The buffer held in m_inputTensors is allocated and set by CreateInputMemory() and SetInputFromMemory().
219 */
220 std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
221 void* inputBuffer = inputDevice->AllocateTensorBuffer(length, inputTensor);
222 if (inputBuffer == nullptr) {
223 LOGE("SetInput failed, error happened when allocating input device buffer.");
224 return OH_NN_MEMORY_ERROR;
225 }
226
227 errno_t status = memcpy_s(inputBuffer, dataLength, buffer, dataLength);
228 if (status != EOK) {
229 LOGE("SetInput failed, copy data from user buffer failed. Error code: %d.", status);
230 inputDevice->ReleaseBuffer(inputBuffer);
231 return OH_NN_MEMORY_ERROR;
232 }
233
234 SetInputTensorWithNewBuffer(index, inputTensor, inputBuffer, length, true);
235 m_isRun = false;
236 return OH_NN_SUCCESS;
237 }
238
239
SetInputFromMemory(uint32_t index,const OH_NN_Tensor & nnTensor,const OH_NN_Memory & memory)240 OH_NN_ReturnCode Executor::SetInputFromMemory(uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory)
241 {
242 auto nnRet = CheckInputDimRanges(index, nnTensor);
243 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
244 LOGI("Skip input dimension bounds check.");
245 } else if (nnRet != OH_NN_SUCCESS) {
246 LOGE("SetInputFromMemory failed, Check the range of the %uth input dimension ranges failed.", index);
247 return nnRet;
248 }
249
250 // Build a input tensor
251 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
252 if (inputTensor == nullptr) {
253 LOGE("SetInputFromMemory failed, error happened when creating NNTensor.");
254 return OH_NN_MEMORY_ERROR;
255 }
256
257 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
258 if (ret != OH_NN_SUCCESS) {
259 LOGE("SetInputFromMemory failed, please check input index or nnTensor");
260 return ret;
261 }
262
263 // check data length
264 size_t dataLength = inputTensor->GetDataLength();
265 if (memory.length == 0 || memory.length < dataLength) {
266 LOGE("SetInputFromMemory failed,"
267 " the length in the given memory is too small to store the input nnTensor data.");
268 return OH_NN_INVALID_PARAMETER;
269 }
270
271 SetInputTensorWithNewBuffer(index, inputTensor, const_cast<const void*>(memory.data), memory.length, false);
272 m_isRun = false;
273 return OH_NN_SUCCESS;
274 }
275
276
SetOutput(uint32_t index,void * buffer,size_t length)277 OH_NN_ReturnCode Executor::SetOutput(uint32_t index, void* buffer, size_t length)
278 {
279 if (index >= m_modelOutputs.size()) {
280 LOGE("SetOutput failed, output index is out of range.");
281 return OH_NN_INVALID_PARAMETER;
282 }
283
284 size_t dataLength = m_modelOutputs[index]->GetDataLength();
285 if (length == 0 || length < dataLength) {
286 LOGE("SetOutput failed, the given buffer length is too small to store the output tensor data.");
287 return OH_NN_INVALID_PARAMETER;
288 }
289
290 // If output tensor does not exist, or inner device buffer size is not enough,
291 // or device buffer is set by SetOutputFromMemory() before,
292 // allocate a new device buffer and set it to output tensor, and update the user buffer.
293 std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
294 if (m_outputTensors.find(index) != m_outputTensors.end()) {
295 if (m_outputTensors[index].isInnerMem) {
296 size_t curBufferLength = m_outputTensors[index].tensor->GetBufferLength();
297 if (length <= curBufferLength) {
298 // If current device buffer size is enough, only update the user buffer.
299 m_outputTensors[index].userBuffer = buffer;
300 m_outputTensors[index].userBufferLength = length;
301 m_isRun = false;
302 return OH_NN_SUCCESS;
303 } else {
304 // If current device buffer size is not enough,
305 // release current device buffer and then allocate a new one below.
306 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
307 outputDevice->ReleaseBuffer(curBuffer);
308 }
309 }
310 } else {
311 // If output tensor does not exist, create a new null output tensor.
312 ExeTensor exeTensor;
313 m_outputTensors[index] = exeTensor;
314 m_outputTensors[index].tensor = m_modelOutputs[index];
315 }
316
317 void* deviceOutputBuffer = outputDevice->AllocateTensorBuffer(length, m_outputTensors[index].tensor);
318 if (deviceOutputBuffer == nullptr) {
319 LOGE("SetOutput failed, allocating output device buffer failed.");
320 return OH_NN_MEMORY_ERROR;
321 }
322
323 m_outputTensors[index].tensor->SetBuffer(deviceOutputBuffer, length);
324 m_outputTensors[index].userBuffer = buffer;
325 m_outputTensors[index].userBufferLength = length;
326 m_outputTensors[index].isInnerMem = true;
327 m_isRun = false;
328 return OH_NN_SUCCESS;
329 }
330
331
SetOutputFromMemory(uint32_t index,const OH_NN_Memory & memory)332 OH_NN_ReturnCode Executor::SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory)
333 {
334 if (index >= m_modelOutputs.size()) {
335 LOGE("SetOutputFromMemory failed, output index is out of range.");
336 return OH_NN_INVALID_PARAMETER;
337 }
338
339 size_t dataLength = m_modelOutputs[index]->GetDataLength();
340 if (memory.length == 0 || memory.length < dataLength) {
341 LOGE("SetOutputFromMemory failed, the memory is too small to store the output tensor data.");
342 return OH_NN_INVALID_PARAMETER;
343 }
344
345 if (m_outputTensors.find(index) != m_outputTensors.end()) {
346 if (m_outputTensors[index].isInnerMem) {
347 // If it is inner buffer, releate it
348 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
349 std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
350 outputDevice->ReleaseBuffer(curBuffer);
351 }
352 } else {
353 // If output tensor does not exist, create a new null output tensor.
354 ExeTensor exeTensor;
355 m_outputTensors[index] = exeTensor;
356 m_outputTensors[index].tensor = m_modelOutputs[index];
357 }
358
359 // Set the output tensor with memory
360 m_outputTensors[index].tensor->SetBuffer(const_cast<const void*>(memory.data), memory.length);
361 m_outputTensors[index].userBuffer = nullptr;
362 m_outputTensors[index].userBufferLength = 0;
363 m_outputTensors[index].isInnerMem = false;
364 m_isRun = false;
365 return OH_NN_SUCCESS;
366 }
367
368
GetOutputShape(uint32_t index,int32_t ** dimensions,uint32_t & dimensionCount)369 OH_NN_ReturnCode Executor::GetOutputShape(uint32_t index, int32_t** dimensions, uint32_t& dimensionCount)
370 {
371 if (!m_isRun) {
372 LOGE("GetOutputShape failed, cannot get output dimensions before Run.");
373 return OH_NN_OPERATION_FORBIDDEN;
374 }
375
376 if (index >= m_modelOutputs.size()) {
377 LOGE("GetOutputShape failed, output index is out of range.");
378 return OH_NN_INVALID_PARAMETER;
379 }
380
381 if (m_outputTensors.find(index) == m_outputTensors.end()) {
382 LOGE("GetOutputShape failed, output has not been set. Output index: %u.", index);
383 return OH_NN_INVALID_PARAMETER;
384 }
385
386 m_outputDimensions[index] = m_outputTensors[index].tensor->GetDimensions();
387 *dimensions = m_outputDimensions[index].data();
388 dimensionCount = m_outputDimensions[index].size();
389
390 return OH_NN_SUCCESS;
391 }
392
393
CreateInputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)394 OH_NN_ReturnCode Executor::CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
395 {
396 if (index >= m_modelInputs.size()) {
397 LOGE("CreateInputMemory failed, input index is out of range.");
398 return OH_NN_INVALID_PARAMETER;
399 }
400
401 // Allocate device buffer
402 std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
403 void* deviceInputBuffer = inputDevice->AllocateTensorBuffer(length, m_modelInputs[index]);
404 if (deviceInputBuffer == nullptr) {
405 LOGE("CreateInputMemory failed, allocating intput device buffer failed.");
406 return OH_NN_MEMORY_ERROR;
407 }
408
409 *memory = new(std::nothrow) OH_NN_Memory{deviceInputBuffer, length};
410 if (*memory == nullptr) {
411 LOGE("CreateInputMemory failed, constructing OH_NN_Memory failed.");
412 inputDevice->ReleaseBuffer(deviceInputBuffer);
413 return OH_NN_MEMORY_ERROR;
414 }
415
416 // Save the buffer address for check when destroying it.
417 m_inputCreatedMem[index].emplace_back(deviceInputBuffer);
418
419 return OH_NN_SUCCESS;
420 }
421
422
DestroyInputMemory(uint32_t index,OH_NN_Memory ** memory)423 OH_NN_ReturnCode Executor::DestroyInputMemory(uint32_t index, OH_NN_Memory** memory)
424 {
425 if (index >= m_modelInputs.size()) {
426 LOGE("DestroyInputMemory failed, input index is out of range.");
427 return OH_NN_INVALID_PARAMETER;
428 }
429
430 if (m_inputCreatedMem.find(index) == m_inputCreatedMem.end()) {
431 LOGE("DestroyInputMemory failed, the memory has not been created with the index.");
432 return OH_NN_INVALID_PARAMETER;
433 }
434
435 std::vector<void*>& inputCreatedMem = m_inputCreatedMem[index];
436 auto pos = std::find(inputCreatedMem.begin(), inputCreatedMem.end(), (*memory)->data);
437 if (pos == inputCreatedMem.end()) {
438 LOGE("DestroyInputMemory failed, the index does not match the memory.");
439 return OH_NN_INVALID_PARAMETER;
440 }
441
442 std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
443 auto ret = inputDevice->ReleaseBuffer((*memory)->data);
444 if (ret != OH_NN_SUCCESS) {
445 LOGE("Release input buffer failed.");
446 return ret;
447 }
448
449 inputCreatedMem.erase(pos);
450 delete *memory;
451 *memory = nullptr;
452
453 return OH_NN_SUCCESS;
454 }
455
456
CreateOutputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)457 OH_NN_ReturnCode Executor::CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
458 {
459 if (index >= m_modelOutputs.size()) {
460 LOGE("CreateOutputMemory failed, output index is out of range.");
461 return OH_NN_INVALID_PARAMETER;
462 }
463
464 // Allocate device buffer
465 std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
466 void* deviceOutputBuffer = outputDevice->AllocateTensorBuffer(length, m_modelOutputs[index]);
467 if (deviceOutputBuffer == nullptr) {
468 LOGE("CreateOutputMemory failed, allocating output device buffer failed.");
469 return OH_NN_MEMORY_ERROR;
470 }
471
472 *memory = new(std::nothrow) OH_NN_Memory{deviceOutputBuffer, length};
473 if (*memory == nullptr) {
474 LOGE("CreateOutputMemory failed, constructing OH_NN_Memory failed.");
475 outputDevice->ReleaseBuffer(deviceOutputBuffer);
476 return OH_NN_MEMORY_ERROR;
477 }
478
479 // Save the buffer address for check when destroying it.
480 m_outputCreatedMem[index].emplace_back(deviceOutputBuffer);
481
482 return OH_NN_SUCCESS;
483 }
484
485
DestroyOutputMemory(uint32_t index,OH_NN_Memory ** memory)486 OH_NN_ReturnCode Executor::DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory)
487 {
488 if (index >= m_modelOutputs.size()) {
489 LOGE("DestroyOutputMemory failed, output index is out of range.");
490 return OH_NN_INVALID_PARAMETER;
491 }
492
493 if (m_outputCreatedMem.find(index) == m_outputCreatedMem.end()) {
494 LOGE("DestroyOutputMemory failed, the memory has not been created with the index.");
495 return OH_NN_INVALID_PARAMETER;
496 }
497
498 std::vector<void*>& outputCreatedMem = m_outputCreatedMem[index];
499 auto pos = std::find(outputCreatedMem.begin(), outputCreatedMem.end(), (*memory)->data);
500 if (pos == outputCreatedMem.end()) {
501 LOGE("DestroyOutputMemory failed, the index does not match the memory.");
502 return OH_NN_INVALID_PARAMETER;
503 }
504
505 std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
506 auto ret = outputDevice->ReleaseBuffer((*memory)->data);
507 if (ret != OH_NN_SUCCESS) {
508 LOGE("Release output buffer failed.");
509 return ret;
510 }
511
512 outputCreatedMem.erase(pos);
513 delete *memory;
514 *memory = nullptr;
515
516 return OH_NN_SUCCESS;
517 }
518
519
Run()520 OH_NN_ReturnCode Executor::Run()
521 {
522 NNRT_TRACE_NAME("Execution");
523 if (m_modelInputs.size() != m_inputTensors.size()) {
524 LOGE("Run failed, some input tensors have not been set.");
525 return OH_NN_INVALID_PARAMETER;
526 }
527 if (m_modelOutputs.size() != m_outputTensors.size()) {
528 LOGE("Run failed, some output tensors have not been set.");
529 return OH_NN_INVALID_PARAMETER;
530 }
531
532 // Build the NNTensor pointer vector: inputTensors and outputTensors
533 std::vector<std::shared_ptr<NNTensor>> inputTensors;
534 std::vector<std::shared_ptr<NNTensor>> outputTensors;
535 size_t inputSize = m_inputTensors.size();
536 size_t outputSize = m_outputTensors.size();
537 for (size_t i = 0; i < inputSize; ++i) {
538 inputTensors.emplace_back(m_inputTensors[i].tensor);
539 }
540 for (size_t i = 0; i < outputSize; ++i) {
541 outputTensors.emplace_back(m_outputTensors[i].tensor);
542 }
543
544 // Predict
545 auto ret = m_executionPlan->Run(inputTensors, outputTensors);
546 if (ret != OH_NN_SUCCESS) {
547 LOGE("Run failed, error happened when executing the inference.");
548 return ret;
549 }
550
551 errno_t status{EOK};
552 // Copy inner device buffer to user buffer if using SetOutput()
553 for (size_t i = 0; i < outputSize; ++i) {
554 if (m_outputTensors[i].isInnerMem) {
555 auto size = outputTensors[i]->GetDataLength();
556 if (size > m_outputTensors[i].userBufferLength) {
557 LOGE("Output buffer size is not enough. Your size=%zu, but actual output size=%zu.",
558 m_outputTensors[i].userBufferLength, size);
559 return OH_NN_INVALID_PARAMETER;
560 }
561
562 void* deviceBuffer = outputTensors[i]->GetBuffer();
563 if (deviceBuffer == nullptr) {
564 LOGE("Output buffer is nullptr.");
565 return OH_NN_FAILED;
566 }
567
568 status = memcpy_s(m_outputTensors[i].userBuffer, m_outputTensors[i].userBufferLength, deviceBuffer, size);
569 if (status != EOK) {
570 LOGE("Run failed, memory copy from device buffer to user buffer failed. Error code: %d.", status);
571 return OH_NN_MEMORY_ERROR;
572 }
573 }
574 }
575
576 m_isRun = true;
577 return OH_NN_SUCCESS;
578 }
579
~Executor()580 Executor::~Executor()
581 {
582 std::shared_ptr<Device> inputDevice;
583 for (auto& it : m_inputTensors) {
584 inputDevice = m_executionPlan->GetInputDevice();
585 if ((it.second).isInnerMem) {
586 inputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
587 }
588 (it.second).tensor->SetBuffer(nullptr, 0);
589 (it.second).tensor.reset();
590 (it.second).userBuffer = nullptr;
591 }
592 m_inputTensors.clear();
593
594 std::shared_ptr<Device> outputDevice;
595 for (auto& it : m_outputTensors) {
596 outputDevice = m_executionPlan->GetOutputDevice();
597 if ((it.second).isInnerMem) {
598 outputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
599 }
600 (it.second).tensor->SetBuffer(nullptr, 0);
601 (it.second).tensor.reset();
602 (it.second).userBuffer = nullptr;
603 }
604 m_outputTensors.clear();
605
606 for (auto& it : m_inputCreatedMem) {
607 it.second.clear();
608 }
609 m_inputCreatedMem.clear();
610
611 for (auto& it : m_outputCreatedMem) {
612 it.second.clear();
613 }
614 m_outputCreatedMem.clear();
615
616 m_outputDimensions.clear();
617 m_modelInputs.clear();
618 m_modelOutputs.clear();
619 }
620 } // namespace NeuralNetworkRuntime
621 } // namespace OHOS
622