• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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