text: /**
  * Copyright 2020 Huawei Technologies Co., Ltd
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  #include "op_runner.h"
  
  #include <limits>
  
  #include "common.h"
  
  using namespace std;
  
  
  OpRunner::OpRunner(OperatorDesc *opDesc, bool isDevice) :
      opDesc_(opDesc), isDevice_(isDevice)
  {
      numInputs_ = opDesc->inputDesc.size();
      numOutputs_ = opDesc->outputDesc.size();
  }
  
  OpRunner::~OpRunner()
  {
      for (size_t i = 0; i < numInputs_; ++i) {
          aclDestroyDataBuffer(inputBuffers_[i]);
          aclrtFree(devInputs_[i]);
          if (isDevice_) {
              aclrtFree(hostInputs_[i]);
          } else {
              aclrtFreeHost(hostInputs_[i]);
          }
      }
  
      for (size_t i = 0; i < numOutputs_; ++i) {
          aclDestroyDataBuffer(outputBuffers_[i]);
          aclrtFree(devOutputs_[i]);
          if (isDevice_) {
              aclrtFree(hostOutputs_[i]);
          } else {
              aclrtFreeHost(hostOutputs_[i]);
          }
      }
  }
  
  bool OpRunner::Init()
  {
      for (size_t i = 0; i < numInputs_; ++i) {
          auto size = GetInputSize(i);
          void *devMem = nullptr;
          if (aclrtMalloc(&devMem, size, ACL_MEM_MALLOC_NORMAL_ONLY) != ACL_SUCCESS) {
              ERROR_LOG("Malloc device memory for input[%zu] failed", i);
              return false;
          }
          devInputs_.emplace_back(devMem);
          inputBuffers_.emplace_back(aclCreateDataBuffer(devMem, size));
  
          void *hostMem = nullptr;
          if (isDevice_) {
              if (aclrtMalloc(&hostMem, size, ACL_MEM_MALLOC_NORMAL_ONLY)!= ACL_SUCCESS) {
                  ERROR_LOG("Malloc device memory for input[%zu] failed", i);
                  return false;
              }
          } else {
              if (aclrtMallocHost(&hostMem, size) != ACL_SUCCESS) {
                  ERROR_LOG("Malloc device memory for input[%zu] failed", i);
                  return false;
              }
          }
          if (hostMem == nullptr) {
              ERROR_LOG("Malloc memory for input[%zu] failed", i);
              return false;
          }
          hostInputs_.emplace_back(hostMem);
      }
  
      for (size_t i = 0; i < numOutputs_; ++i) {
          auto size = GetOutputSize(i);
          void *devMem = nullptr;
          if (aclrtMalloc(&devMem, size, ACL_MEM_MALLOC_NORMAL_ONLY) != ACL_SUCCESS) {
              ERROR_LOG("Malloc device memory for output[%zu] failed", i);
              return false;
          }
          devOutputs_.emplace_back(devMem);
          outputBuffers_.emplace_back(aclCreateDataBuffer(devMem, size));
  
          void *hostOutput = nullptr;
          if (isDevice_) {
              if (aclrtMalloc(&hostOutput, size, ACL_MEM_MALLOC_NORMAL_ONLY)!= ACL_SUCCESS) {
                  ERROR_LOG("Malloc device memory for output[%zu] failed", i);
                  return false;
              }
          } else {
              if (aclrtMallocHost(&hostOutput, size) != ACL_SUCCESS) {
                  ERROR_LOG("Malloc device memory for output[%zu] failed", i);
                  return false;
              }
          }
          if (hostOutput == nullptr) {
              ERROR_LOG("Malloc host memory for output[%zu] failed", i);
              return false;
          }
          hostOutputs_.emplace_back(hostOutput);
      }
  
      return true;
  }
  
  size_t OpRunner::NumInputs()
  {
      return numInputs_;
  }
  
  size_t OpRunner::NumOutputs()
  {
      return numOutputs_;
  }
  
  size_t OpRunner::GetInputSize(size_t index) const
  {
      if (index >= opDesc_->inputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numInputs = %zu", index, numInputs_);
          return 0;
      }
  
      return aclGetTensorDescSize(opDesc_->inputDesc[index]);
  }
  
  std::vector<int64_t> OpRunner::GetInputShape(size_t index) const
  {
      std::vector<int64_t> ret;
      if (index >= opDesc_->inputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numInputs = %zu", index, numInputs_);
          return ret;
      }
  
      auto desc = opDesc_->inputDesc[index];
      for (size_t i = 0; i < aclGetTensorDescNumDims(desc); ++i) {
          int64_t dimSize;
          if (aclGetTensorDescDimV2(desc, i, &dimSize) != 0) {
              ERROR_LOG("get dimension size from tensor desc failed, dimension index = %zu", i);
              ret.clear();
              return ret;
          }
          ret.emplace_back(dimSize);
      }
  
      return ret;
  }
  
  std::vector<int64_t> OpRunner::GetOutputShape(size_t index) const
  {
      std::vector<int64_t> ret;
      if (index >= opDesc_->outputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numOutputs_);
          return ret;
      }
  
      auto desc = opDesc_->outputDesc[index];
      for (size_t i = 0; i < aclGetTensorDescNumDims(desc); ++i) {
          int64_t dimSize;
          if (aclGetTensorDescDimV2(desc, i, &dimSize) != 0) {
              ERROR_LOG("get dimension size from tensor desc failed, dimension index = %zu", i);
              ret.clear();
              return ret;
          }
          ret.emplace_back(dimSize);
      }
      return ret;
  }
  
  size_t OpRunner::GetInputElementCount(size_t index) const
  {
      if (index >= opDesc_->inputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numInputs = %zu", index, numInputs_);
          return 0;
      }
  
      return aclGetTensorDescElementCount(opDesc_->inputDesc[index]);
  }
  
  size_t OpRunner::GetOutputElementCount(size_t index) const
  {
      if (index >= opDesc_->outputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numOutputs_);
          return 0;
      }
  
      return aclGetTensorDescElementCount(opDesc_->outputDesc[index]);
  }
  
  size_t OpRunner::GetOutputSize(size_t index) const
  {
      if (index >= opDesc_->outputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numOutputs_);
          return 0;
      }
  
      return aclGetTensorDescSize(opDesc_->outputDesc[index]);
  }
  
  bool OpRunner::RunOp()
  {
      for (size_t i = 0; i < numInputs_; ++i) {
          auto size = GetInputSize(i);
          aclrtMemcpyKind kind = ACL_MEMCPY_HOST_TO_DEVICE;
          if (isDevice_) {
              kind = ACL_MEMCPY_DEVICE_TO_DEVICE;
          }
          if (aclrtMemcpy(devInputs_[i], size, hostInputs_[i], size, kind) != ACL_SUCCESS) {
              ERROR_LOG("Copy input[%zu] failed", i);
              return false;
          }
          INFO_LOG("Copy input[%zu] success", i);
      }
  
      aclrtStream stream = nullptr;
      if (aclrtCreateStream(&stream) != ACL_SUCCESS) {
          ERROR_LOG("Create stream failed");
          return false;
      }
      INFO_LOG("Create stream success");
  
      auto ret = aclopExecuteV2(opDesc_->opType.c_str(),
                              numInputs_,
                              opDesc_->inputDesc.data(),
                              inputBuffers_.data(),
                              numOutputs_,
                              opDesc_->outputDesc.data(),
                              outputBuffers_.data(),
                              opDesc_->opAttr,
                              stream);
      if (ret == ACL_ERROR_OP_TYPE_NOT_MATCH || ret == ACL_ERROR_OP_INPUT_NOT_MATCH ||
          ret == ACL_ERROR_OP_OUTPUT_NOT_MATCH || ret == ACL_ERROR_OP_ATTR_NOT_MATCH) {
          ERROR_LOG("[%s] op with the given description is not compiled. Please run atc first", opDesc_->opType.c_str());
          (void) aclrtDestroyStream(stream);
          return false;
      } else if (ret != ACL_SUCCESS) {
          ERROR_LOG("Execute %s failed. ret = %d", opDesc_->opType.c_str(), ret);
          (void) aclrtDestroyStream(stream);
          return false;
      }
      INFO_LOG("Execute %s success", opDesc_->opType.c_str());
  
      if (aclrtSynchronizeStream(stream) != ACL_SUCCESS) {
          (void) aclrtDestroyStream(stream);
          ERROR_LOG("Synchronize stream failed");
          return false;
      }
      INFO_LOG("Synchronize stream success");
      (void) aclrtDestroyStream(stream);
  
      for (size_t i = 0; i < numOutputs_; ++i) {
          auto size = GetOutputSize(i);
          aclrtMemcpyKind kind = ACL_MEMCPY_DEVICE_TO_HOST;
          if (isDevice_) {
              kind = ACL_MEMCPY_DEVICE_TO_DEVICE;
          }
          if (aclrtMemcpy(hostOutputs_[i], size, devOutputs_[i], size, kind) != ACL_SUCCESS) {
              ERROR_LOG("Copy output[%zu] failed", i);
              return false;
          }
  
          INFO_LOG("Copy output[%zu] success", i);
      }
  
      return true;
  }
  
  
  template<typename T>
  void DoPrintData(const T *data, size_t count, size_t elementsPerRow)
  {
      for (size_t i = 0; i < count; ++i) {
          std::cout << std::setw(10) << data[i];
          if (i % elementsPerRow == elementsPerRow - 1) {
              std::cout << std::endl;
          }
      }
  }
  
  void DoPrintFp16Data(const aclFloat16 *data, size_t count, size_t elementsPerRow)
  {
      for (size_t i = 0; i < count; ++i) {
          std::cout << std::setw(10) << std::setprecision(4) << aclFloat16ToFloat(data[i]);
          if (i % elementsPerRow == elementsPerRow - 1) {
              std::cout << std::endl;
          }
      }
  }
  
  void PrintData(const void *data, size_t count, aclDataType dataType, size_t elementsPerRow)
  {
      if (data == nullptr) {
          ERROR_LOG("Print data failed. data is nullptr");
          return;
      }
  
      switch (dataType) {
          case ACL_BOOL:
              DoPrintData(reinterpret_cast<const bool *>(data), count, elementsPerRow);
              break;
          case ACL_INT8:
              DoPrintData(reinterpret_cast<const int8_t *>(data), count, elementsPerRow);
              break;
          case ACL_UINT8:
              DoPrintData(reinterpret_cast<const uint8_t *>(data), count, elementsPerRow);
              break;
          case ACL_INT16:
              DoPrintData(reinterpret_cast<const int16_t *>(data), count, elementsPerRow);
              break;
          case ACL_UINT16:
              DoPrintData(reinterpret_cast<const uint16_t *>(data), count, elementsPerRow);
              break;
          case ACL_INT32:
              DoPrintData(reinterpret_cast<const int32_t *>(data), count, elementsPerRow);
              break;
          case ACL_UINT32:
              DoPrintData(reinterpret_cast<const uint32_t *>(data), count, elementsPerRow);
              break;
          case ACL_INT64:
              DoPrintData(reinterpret_cast<const int64_t *>(data), count, elementsPerRow);
              break;
          case ACL_UINT64:
              DoPrintData(reinterpret_cast<const uint64_t *>(data), count, elementsPerRow);
              break;
          case ACL_FLOAT16:
              DoPrintFp16Data(reinterpret_cast<const aclFloat16 *>(data), count, elementsPerRow);
              break;
          case ACL_FLOAT:
              DoPrintData(reinterpret_cast<const float *>(data), count, elementsPerRow);
              break;
          case ACL_DOUBLE:
              DoPrintData(reinterpret_cast<const double *>(data), count, elementsPerRow);
              break;
          default:
              ERROR_LOG("Unsupported type: %d", dataType);
      }
  }
  
  void OpRunner::PrintInput(size_t index, size_t numElementsPerRow)
  {
      if (index >= opDesc_->inputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numInputs_);
          return;
      }
  
      auto desc = opDesc_->inputDesc[index];
      PrintData(hostInputs_[index], GetInputElementCount(index), aclGetTensorDescType(desc), numElementsPerRow);
  }
  
  void OpRunner::PrintOutput(size_t index, size_t numElementsPerRow)
  {
      if (index >= opDesc_->outputDesc.size()) {
          ERROR_LOG("index out of range. index = %zu, numOutputs = %zu", index, numOutputs_);
          return;
      }
  
      auto desc = opDesc_->outputDesc[index];
      PrintData(hostOutputs_[index], GetOutputElementCount(index), aclGetTensorDescType(desc), numElementsPerRow);
  }
  