Program Listing for File predict_api.cpp

Return to documentation for file (/workspace/amdinfer/src/amdinfer/core/predict_api.cpp)

// Copyright 2021 Xilinx, Inc.
// Copyright 2022 Advanced Micro Devices, Inc.
//
// 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 "amdinfer/core/predict_api.hpp"

#include <algorithm>    // for copy
#include <cassert>      // for assert
#include <cstring>      // for memcpy
#include <iterator>     // for back_insert_iterator, back_inse...
#include <tuple>        // for tuple
#include <type_traits>  // for add_const<>::type, decay_t
#include <utility>      // for pair, make_pair, move

#include "amdinfer/build_options.hpp"    // for AMDINFER_ENABLE_TRACING
#include "amdinfer/util/containers.hpp"  // for containerProduct

namespace amdinfer {

void RequestParameters::put(const std::string &key, bool value) {
  this->parameters_.try_emplace(key, value);
}

void RequestParameters::put(const std::string &key, double value) {
  this->parameters_.try_emplace(key, value);
}

void RequestParameters::put(const std::string &key, int32_t value) {
  this->parameters_.try_emplace(key, value);
}

void RequestParameters::put(const std::string &key, const std::string &value) {
  this->parameters_.try_emplace(key, value);
}

void RequestParameters::put(const std::string &key, const char *value) {
  this->parameters_.try_emplace(key, std::string{value});
}

void RequestParameters::erase(const std::string &key) {
  if (this->parameters_.find(key) != this->parameters_.end()) {
    this->parameters_.erase(key);
  }
}

bool RequestParameters::has(const std::string &key) {
  return this->parameters_.find(key) != this->parameters_.end();
}

size_t RequestParameters::size() const { return parameters_.size(); }

bool RequestParameters::empty() const { return parameters_.empty(); }

std::map<std::string, Parameter, std::less<>> RequestParameters::data() const {
  return parameters_;
}

size_t RequestParameters::serializeSize() const {
  // 1 for num of parameters plus 3 for each parameter for type index, key size
  // and value size
  auto size = ((this->size() * 3) + 1) * sizeof(size_t);
  for (const auto &[key, value] : parameters_) {
    size += key.size();
    std::visit(
      [&size](const auto &param) {
        using T = std::decay_t<decltype(param)>;
        if constexpr (std::is_same_v<T, std::string>) {
          size += param.size();
        } else {
          size += sizeof(param);
        }
      },
      value);
  }
  return size;
}

template <typename T>
std::byte *copy(const T &src, std::byte *dst, size_t count) {
  if constexpr (std::is_pointer_v<T>) {
    std::memcpy(dst, src, count);
  } else {
    std::memcpy(dst, &src, count);
  }
  return dst + count;
}

void RequestParameters::serialize(std::byte *data_out) const {
  auto size = this->size();
  std::string foo;
  data_out = copy(size, data_out, sizeof(size_t));
  for (const auto &[key, value] : parameters_) {
    data_out = copy(value.index(), data_out, sizeof(size_t));
    data_out = copy(key.size(), data_out, sizeof(size_t));
    std::visit(
      [&](const auto &param) {
        using T = std::decay_t<decltype(param)>;
        if constexpr (std::is_same_v<T, std::string>) {
          data_out = copy(param.size(), data_out, sizeof(size_t));
        } else {
          data_out = copy(sizeof(param), data_out, sizeof(size_t));
        }
      },
      value);
  }
  for (const auto &[key, value] : parameters_) {
    data_out = copy(key.c_str(), data_out, key.size());
    std::visit(
      [&](const auto &param) {
        using T = std::decay_t<decltype(param)>;
        if constexpr (std::is_same_v<T, std::string>) {
          data_out = copy(param.c_str(), data_out, param.size());
        } else {
          data_out = copy(param, data_out, sizeof(param));
        }
      },
      value);
  }
}

template <typename... Ts>
[[nodiscard]] std::variant<Ts...> expandType(std::size_t i) {
  assert(i < sizeof...(Ts));
  static constexpr auto kTable =
    std::array{+[]() { return std::variant<Ts...>{Ts{}}; }...};
  return kTable.at(i)();
}

void RequestParameters::deserialize(const std::byte *data_in) {
  parameters_.clear();

  auto size = std::to_integer<size_t>(*data_in);
  data_in += sizeof(size_t);
  std::vector<std::tuple<size_t, size_t, size_t>> params;
  params.reserve(size);
  for (auto i = 0U; i < size; i++) {
    auto index = std::to_integer<size_t>(*data_in);
    data_in += sizeof(size_t);
    auto key_size = std::to_integer<size_t>(*data_in);
    data_in += sizeof(size_t);
    auto data_size = std::to_integer<size_t>(*data_in);
    data_in += sizeof(size_t);
    params.emplace_back(index, key_size, data_size);
  }
  for (const auto &[index, key_size, data_size] : params) {
    std::string key;
    key.resize(key_size);
    std::memcpy(key.data(), data_in, key_size);
    data_in += key_size;
    Parameter param = expandType<bool, int32_t, double, std::string>(index);
    const auto &n = data_size;
    std::visit(
      [&](auto &value) {
        using T = std::decay_t<decltype(value)>;
        if constexpr (std::is_same_v<T, std::string>) {
          value.resize(n);
          std::memcpy(value.data(), data_in, n);
          data_in += n;
        } else {
          std::memcpy(&value, data_in, n);
          data_in += n;
        }
      },
      param);
    parameters_.try_emplace(key, param);
  }
}

void InferenceRequest::setCallback(Callback &&callback) {
  callback_ = std::move(callback);
}

void InferenceRequest::runCallbackOnce(const InferenceResponse &response) {
  if (this->callback_ != nullptr) {
    (this->callback_)(response);
    this->callback_ = nullptr;
  }
}

void InferenceRequest::runCallback(const InferenceResponse &response) {
  (this->callback_)(response);
}

void InferenceRequest::runCallbackError(std::string_view error_msg) {
  this->runCallback(InferenceResponse(std::string{error_msg}));
}

void InferenceRequest::addInputTensor(void *data,
                                      const std::vector<uint64_t> &shape,
                                      DataType data_type,
                                      const std::string &name) {
  this->inputs_.emplace_back(data, shape, data_type, name);
}

void InferenceRequest::addInputTensor(InferenceRequestInput input) {
  this->inputs_.push_back(std::move(input));
}

const std::vector<InferenceRequestInput> &InferenceRequest::getInputs() const {
  return this->inputs_;
}

size_t InferenceRequest::getInputSize() const { return this->inputs_.size(); }

const std::vector<InferenceRequestOutput> &InferenceRequest::getOutputs()
  const {
  return this->outputs_;
}

void InferenceRequest::addOutputTensor(const InferenceRequestOutput &output) {
  this->outputs_.push_back(output);
}

InferenceRequestInput::InferenceRequestInput(void *data,
                                             std::vector<uint64_t> shape,
                                             DataType data_type,
                                             std::string name)
  : data_type_(data_type) {
  this->data_ = data;
  this->shape_ = std::move(shape);
  this->name_ = std::move(name);
  this->parameters_ = std::make_unique<RequestParameters>();
}

InferenceRequestInput::InferenceRequestInput() : data_type_(DataType::Uint32) {
  this->data_ = nullptr;
  this->name_ = "";
  this->parameters_ = std::make_unique<RequestParameters>();
}

void InferenceRequestInput::setName(std::string name) {
  this->name_ = std::move(name);
}

void InferenceRequestInput::setDatatype(DataType type) {
  this->data_type_ = type;
}

void InferenceRequestInput::setData(void *buffer) { this->data_ = buffer; }

void InferenceRequestInput::setData(std::vector<std::byte> &&buffer) {
  this->shared_data_ = std::move(buffer);
}

bool InferenceRequestInput::sharedData() const {
  return this->shared_data_.empty();
}

size_t InferenceRequestInput::getSize() const {
  return util::containerProduct(shape_);
}

void *InferenceRequestInput::getData() const {
  if (!this->shared_data_.empty()) {
    return (void *)shared_data_.data();  // NOLINT(google-readability-casting)
  }
  return this->data_;
}

struct InferenceRequestInputSizes {
  size_t name;
  size_t shape;
  size_t data_type;
  size_t parameters;
  size_t data;
  size_t shared_data;
};

size_t InferenceRequestInput::serializeSize() const {
  auto size = sizeof(InferenceRequestInputSizes);
  size += name_.length();
  size += shape_.size() * sizeof(uint64_t);
  size += sizeof(uint8_t);
  size += parameters_->serializeSize();
  if (!shared_data_.empty()) {
    size += shared_data_.size();
  } else {
    size += sizeof(data_);
  }
  return size;
}

void InferenceRequestInput::serialize(std::byte *data_out) const {
  InferenceRequestInputSizes metadata{name_.length(),
                                      shape_.size() * sizeof(uint64_t),
                                      sizeof(uint8_t),
                                      parameters_->serializeSize(),
                                      0,
                                      0};
  if (!shared_data_.empty()) {
    metadata.shared_data = this->getSize() * data_type_.size();
  } else {
    metadata.data = sizeof(data_);
  }
  data_out = copy(metadata, data_out, sizeof(InferenceRequestInputSizes));
  data_out = copy(name_.c_str(), data_out, metadata.name);
  data_out = copy(shape_.data(), data_out, metadata.shape);
  data_out =
    copy(static_cast<uint8_t>(data_type_), data_out, metadata.data_type);
  parameters_->serialize(data_out);
  data_out += metadata.parameters;
  if (!shared_data_.empty()) {
    amdinfer::copy(shared_data_.data(), data_out, metadata.shared_data);
  } else {
    std::memcpy(data_out, &data_, metadata.data);
  }
}

void InferenceRequestInput::deserialize(const std::byte *data_in) {
  const auto metadata =
    *reinterpret_cast<const InferenceRequestInputSizes *>(data_in);
  data_in += sizeof(InferenceRequestInputSizes);

  name_.resize(metadata.name);
  std::memcpy(name_.data(), data_in, metadata.name);
  data_in += metadata.name;

  shape_.resize(metadata.shape / sizeof(uint64_t));
  std::memcpy(shape_.data(), data_in, metadata.shape);
  data_in += metadata.shape;

  uint8_t type = 0;
  std::memcpy(&type, data_in, metadata.data_type);
  data_in += metadata.data_type;
  data_type_ = static_cast<DataType::Value>(type);

  parameters_ = std::make_shared<RequestParameters>();
  parameters_->deserialize(data_in);
  data_in += parameters_->serializeSize();

  if (metadata.shared_data != 0) {
    shared_data_.resize(metadata.shared_data);
    std::memcpy(shared_data_.data(), data_in, metadata.shared_data);
  } else {
    std::memcpy(&data_, data_in, metadata.data);
  }
}

InferenceRequestOutput::InferenceRequestOutput() {
  this->data_ = nullptr;
  this->name_ = "";
  this->parameters_ = std::make_unique<RequestParameters>();
}

void InferenceRequestOutput::setName(const std::string &name) {
  this->name_ = name;
}

InferenceResponse::InferenceResponse() {
  this->parameters_ = std::make_unique<RequestParameters>();
}

InferenceResponse::InferenceResponse(const std::string &error_msg) {
  this->parameters_ = nullptr;
  this->error_msg_ = error_msg;
}

void InferenceResponse::setID(const std::string &id) { this->id_ = id; }

void InferenceResponse::setModel(const std::string &model) {
  this->model_ = model;
}

std::string InferenceResponse::getModel() { return this->model_; }

bool InferenceResponse::isError() const { return !this->error_msg_.empty(); }

std::string InferenceResponse::getError() const { return this->error_msg_; }

void InferenceResponse::addOutput(const InferenceResponseOutput &output) {
  this->outputs_.push_back(output);
}

std::vector<InferenceResponseOutput> InferenceResponse::getOutputs() const {
  return this->outputs_;
}

#ifdef AMDINFER_ENABLE_TRACING
void InferenceResponse::setContext(StringMap &&context) {
  this->context_ = std::move(context);
}

const StringMap &InferenceResponse::getContext() const {
  return this->context_;
}
#endif

ModelMetadataTensor::ModelMetadataTensor(const std::string &name,
                                         DataType datatype,
                                         std::vector<uint64_t> shape)
  : datatype_(datatype) {
  this->name_ = name;
  this->shape_ = std::move(shape);
}

const std::string &ModelMetadataTensor::getName() const { return this->name_; }

const DataType &ModelMetadataTensor::getDataType() const {
  return this->datatype_;
}
const std::vector<uint64_t> &ModelMetadataTensor::getShape() const {
  return this->shape_;
}

ModelMetadata::ModelMetadata(const std::string &name,
                             const std::string &platform) {
  this->name_ = name;
  this->platform_ = platform;
  this->ready_ = false;
}

void ModelMetadata::addInputTensor(const std::string &name, DataType datatype,
                                   std::initializer_list<uint64_t> shape) {
  this->inputs_.emplace_back(name, datatype, shape);
}

void ModelMetadata::addInputTensor(const std::string &name, DataType datatype,
                                   std::vector<int> shape) {
  std::vector<uint64_t> new_shape;
  std::copy(shape.begin(), shape.end(), std::back_inserter(new_shape));
  this->inputs_.emplace_back(name, datatype, new_shape);
}

void ModelMetadata::addOutputTensor(const std::string &name, DataType datatype,
                                    std::initializer_list<uint64_t> shape) {
  this->outputs_.emplace_back(name, datatype, shape);
}

void ModelMetadata::addOutputTensor(const std::string &name, DataType datatype,
                                    std::vector<int> shape) {
  std::vector<uint64_t> new_shape;
  std::copy(shape.begin(), shape.end(), std::back_inserter(new_shape));
  this->outputs_.emplace_back(name, datatype, new_shape);
}

const std::string &ModelMetadata::getName() const { return this->name_; }
void ModelMetadata::setName(const std::string &name) { this->name_ = name; }

void ModelMetadata::setReady(bool ready) { this->ready_ = ready; }

bool ModelMetadata::isReady() const { return this->ready_; }

const std::string &ModelMetadata::getPlatform() const {
  return this->platform_;
}

const std::vector<ModelMetadataTensor> &ModelMetadata::getInputs() const {
  return this->inputs_;
}

const std::vector<ModelMetadataTensor> &ModelMetadata::getOutputs() const {
  return this->outputs_;
}

}  // namespace amdinfer