blob: bbe94636b3a14d3eb6c2d2f6142fdc76468dedf4 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \brief Tiny graph executor that can run graph
* containing only tvm PackedFunc.
* \file graph_executor.h
*/
#ifndef TVM_RUNTIME_GRAPH_EXECUTOR_GRAPH_EXECUTOR_H_
#define TVM_RUNTIME_GRAPH_EXECUTOR_GRAPH_EXECUTOR_H_
#include <dlpack/dlpack.h>
#include <dmlc/json.h>
#include <dmlc/memory_io.h>
#include <tvm/runtime/ndarray.h>
#include <tvm/runtime/packed_func.h>
#include <memory>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
namespace tvm {
namespace runtime {
/*! \brief macro to do C API call */
#define TVM_CCALL(func) \
{ \
int ret = (func); \
ICHECK_EQ(ret, 0) << TVMGetLastError(); \
}
/*! \brief operator attributes about tvm op */
struct TVMOpParam {
std::string func_name;
std::unordered_map<std::string, ObjectRef> attrs;
uint32_t num_inputs;
uint32_t num_outputs;
uint32_t flatten_data;
};
/*!
* \brief Tiny graph executor.
*
* This runtime can be accessible in various languages via
* TVM runtime PackedFunc API.
*/
class TVM_DLL GraphExecutor : public ModuleNode {
struct OpArgs {
std::vector<DLTensor> args;
std::vector<TVMValue> arg_values;
std::vector<int> arg_tcodes;
std::vector<int64_t> shape_data;
};
public:
using ShapeInfo = Map<String, ObjectRef>;
using DtypeInfo = Map<String, ObjectRef>;
/*!
* \brief Get member function to front-end
* \param name The name of the function.
* \param sptr_to_self The pointer to the module node.
* \return The corresponding member function.
*/
virtual PackedFunc GetFunction(const std::string& name, const ObjectPtr<Object>& sptr_to_self);
/*!
* \return The type key of the executor.
*/
const char* type_key() const final { return "GraphExecutor"; }
void Run();
/*!
* \brief Initialize the graph executor with graph and device.
* \param graph_json The execution graph.
* \param module The module containing the compiled functions for the host
* processor.
* \param devs The device of the host and devices where graph nodes will be
* executed on.
* \param lookup_linked_param_func If given, a PackedFunc invoked to lookup linked parameters
* by storage_id. If not given, linked parameters are looked-up using an internal implementation,
* which is not compatible with RPCModules. Default is nullptr.
*/
void Init(const std::string& graph_json, tvm::runtime::Module module,
const std::vector<Device>& devs, const PackedFunc lookup_linked_param_func = nullptr);
/*!
* \brief Get the input index given the name of input.
* \param name The name of the input.
* \return The index of input.
*/
int GetInputIndex(const std::string& name);
/*!
* \brief Get the input info of Graph by parsing the input nodes.
* \return The shape and dtype tuple.
*/
std::tuple<ShapeInfo, DtypeInfo> GetInputInfo() const;
/*!
* \brief Get the output index given the name of output.
* \param name The name of the output.
* \return The index of output.
*/
int GetOutputIndex(const std::string& name);
/*!
* \brief set index-th input to the graph.
* \param index The input index.
* \param data_in The input data.
*/
void SetInput(int index, DLTensor* data_in);
/*!
* \brief set index-th input to the graph without copying the data
* \param index The input index.
* \param data_ref The input data that is referred.
*/
void SetInputZeroCopy(int index, DLTensor* data_ref);
/*!
* \brief set index-th output to the graph without copying the data.
* \param index The output index.
* \param data_ref The output data that is referred.
*/
void SetOutputZeroCopy(int index, DLTensor* data_ref);
/*!
* \brief Get the number of outputs
*
* \return The number of outputs from graph.
*/
int NumOutputs() const;
/*!
* \brief Get the number of inputs
*
* \return The number of inputs to the graph.
*/
int NumInputs() const;
/*!
* \brief Return NDArray for given input index.
* \param index The input index.
*
* \return NDArray corresponding to given input node index.
*/
NDArray GetInput(int index) const;
/*!
* \brief Return NDArray for given output index.
* \param index The output index.
*
* \return NDArray corresponding to given output node index.
*/
NDArray GetOutput(int index) const;
/*!
* \brief Copy index-th output to data_out.
* \param index The output index.
* \param data_out the output data.
*/
void CopyOutputTo(int index, DLTensor* data_out);
/*!
* \brief Load parameters from binary stream
* \param strm The input stream.
*/
void LoadParams(dmlc::Stream* strm);
/*!
* \brief Load parameters from parameter blob.
* \param param_blob A binary blob of parameter.
*/
void LoadParams(const std::string& param_blob);
/*!
* \brief Share parameters from pre-existing GraphExecutor instance.
* \param other A GraphExecutor instance, previously with |LoadParams| called with the
* identical input |param_blob|.
* \param strm The input stream.
*/
void ShareParams(const GraphExecutor& other, dmlc::Stream* strm);
/*!
* \brief Get total number of nodes.
* \return Total number of nodes.
*/
uint32_t GetNumOfNodes() const { return static_cast<uint32_t>(nodes_.size()); }
std::string GetNodeName(uint32_t nid) const { return nodes_[nid].name; }
protected:
// Memory pool entry.
struct PoolEntry {
int device_type;
std::vector<int64_t> shape;
DLDataType dtype;
int param_data_entry;
NDArray linked_param;
std::string scope;
// PoolEntry(int s, int dev_type, void* pre_linked_param) :
// size(s), device_type(dev_type), pre_linked_param(std::move(pre_linked_param)) {}
};
// Node entry
struct NodeEntry {
uint32_t node_id;
uint32_t index;
uint32_t version;
inline bool operator==(const NodeEntry& other) const {
return node_id == other.node_id && index == other.index && version == other.version;
}
// JSON Loader
void Load(dmlc::JSONReader* reader) {
reader->BeginArray();
ICHECK(reader->NextArrayItem()) << "invalid json format";
reader->Read(&node_id);
ICHECK(reader->NextArrayItem()) << "invalid json format";
reader->Read(&index);
if (reader->NextArrayItem()) {
reader->Read(&version);
ICHECK(!reader->NextArrayItem()) << "invalid json format";
} else {
version = 0;
}
}
};
// Node
struct Node {
// operator type in string
std::string op_type;
// name of the op
std::string name;
// parameters
TVMOpParam param;
// inputs
std::vector<NodeEntry> inputs;
// control deps
std::vector<uint32_t> control_deps;
// JSON Loader
void LoadAttrs(dmlc::JSONReader* reader, TVMOpParam* param) {
int bitmask = 0;
std::string key, value;
reader->BeginObject();
while (reader->NextObjectItem(&key)) {
reader->Read(&value);
if (key == "func_name") {
param->func_name = value;
bitmask |= 1;
} else if (key == "num_inputs") {
param->num_inputs = strtoul(value.c_str(), nullptr, 10);
bitmask |= 2;
} else if (key == "num_outputs") {
param->num_outputs = strtoul(value.c_str(), nullptr, 10);
bitmask |= 4;
} else if (key == "flatten_data") {
param->flatten_data = strtoul(value.c_str(), nullptr, 10);
bitmask |= 8;
} else {
param->attrs[key] = String(value);
}
}
ICHECK_EQ(bitmask, 1 | 2 | 4 | 8) << "invalid format";
}
// JSON Loader
void Load(dmlc::JSONReader* reader) {
reader->BeginObject();
int bitmask = 0;
std::string key;
while (reader->NextObjectItem(&key)) {
if (key == "op") {
reader->Read(&op_type);
bitmask |= 1;
} else if (key == "name") {
reader->Read(&name);
bitmask |= 2;
} else if (key == "inputs") {
reader->Read(&inputs);
bitmask |= 4;
} else if (key == "attr" || key == "attrs") {
this->LoadAttrs(reader, &param);
} else if (key == "control_deps") {
reader->Read(&control_deps);
} else {
LOG(FATAL) << "do not support key " << key;
}
}
ICHECK_EQ(bitmask, 1 | 2 | 4) << "invalid format";
}
};
struct GraphAttr {
size_t storage_num_not_alloctaed{0};
std::vector<int> storage_id;
std::vector<int> device_index;
std::vector<std::string> dltype;
std::vector<std::string> storage_scope;
std::vector<std::vector<int64_t>> shape;
// The graph attribute fields.
void Load(dmlc::JSONReader* reader) {
reader->BeginObject();
int bitmask = 0;
std::string key, type;
while (reader->NextObjectItem(&key)) {
if (key == "dltype") {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
ICHECK_EQ(type, "list_str");
ICHECK(reader->NextArrayItem());
reader->Read(&dltype);
ICHECK(!reader->NextArrayItem());
bitmask |= 1;
} else if (key == "storage_id") {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
ICHECK_EQ(type, "list_int");
ICHECK(reader->NextArrayItem());
reader->Read(&storage_id);
ICHECK(!reader->NextArrayItem());
bitmask |= 2;
} else if (key == "storage_scope") {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
ICHECK_EQ(type, "list_str");
ICHECK(reader->NextArrayItem());
reader->Read(&storage_scope);
ICHECK(!reader->NextArrayItem());
bitmask |= 1;
} else if (key == "shape") {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
ICHECK_EQ(type, "list_shape");
ICHECK(reader->NextArrayItem());
reader->Read(&shape);
ICHECK(!reader->NextArrayItem());
bitmask |= 4;
} else if (key == "device_index") {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
ICHECK_EQ(type, "list_int");
ICHECK(reader->NextArrayItem());
reader->Read(&device_index);
ICHECK(!reader->NextArrayItem());
} else {
reader->BeginArray();
ICHECK(reader->NextArrayItem());
reader->Read(&type);
if (type == "list_int") {
ICHECK(reader->NextArrayItem());
std::vector<int> temp;
reader->Read(&temp);
} else if (type == "size_t") {
ICHECK(reader->NextArrayItem());
size_t temp;
reader->Read(&temp);
} else {
LOG(FATAL) << "cannot skip graph attr " << key;
}
ICHECK(!reader->NextArrayItem());
}
}
ICHECK_EQ(bitmask, 1 | 2 | 4) << "invalid format";
}
};
// The graph attribute fields.
void Load(dmlc::JSONReader* reader) {
reader->BeginObject();
int bitmask = 0;
std::string key;
while (reader->NextObjectItem(&key)) {
if (key == "nodes") {
reader->Read(&nodes_);
bitmask |= 1;
} else if (key == "arg_nodes") {
reader->Read(&input_nodes_);
bitmask |= 2;
} else if (key == "node_row_ptr") {
reader->Read(&node_row_ptr_);
bitmask |= 4;
} else if (key == "heads") {
reader->Read(&outputs_);
bitmask |= 8;
} else if (key == "attrs") {
reader->Read(&attrs_);
bitmask |= 16;
} else if (key == "metadata") {
break;
} else {
LOG(FATAL) << "key " << key << " is not supported";
}
}
ICHECK_EQ(bitmask, 1 | 2 | 4 | 8 | 16) << "invalid format";
}
/*! \brief PackedFunc to lookup a linked paramter from a local Module. */
void DefaultLookupLinkedParam(TVMArgs args, TVMRetValue* rv);
/*! \brief Delete NDArray::Container with linked (i.e. static) data. */
static void LinkedNDArrayDeleter(Object* container);
/*! \brief Setup the temporal storage */
void SetupStorage();
/*! \brief Setup the executors. */
void SetupOpExecs();
/*!
* \brief Check the legality of external DLTensor*.
* \param external The external DLTensor*.
* \param eid The data_enrty_ index.
*/
void CheckExternalDLTensor(const DLTensor* external, uint32_t eid) const;
/*!
* \brief Create an execution function given input.
* \param attrs The node attributes.
* \param args The arguments to the functor, including inputs and outputs.
* \return The created executor.
*/
std::pair<std::function<void()>, std::shared_ptr<OpArgs>> CreateTVMOp(
const TVMOpParam& attrs, const std::vector<DLTensor>& args);
// Get node entry index.
uint32_t entry_id(uint32_t nid, uint32_t index) const { return node_row_ptr_[nid] + index; }
// Get node entry index.
uint32_t entry_id(const NodeEntry& e) const { return entry_id(e.node_id, e.index); }
// Number of node entries.
uint32_t num_node_entries() const { return node_row_ptr_.back(); }
/*! \brief The graph nodes. */
std::vector<Node> nodes_;
/*! \brief The argument nodes. */
std::vector<uint32_t> input_nodes_;
/*! \brief The parameter names. */
std::unordered_set<std::string> param_names_;
/*! \brief Map of input names to input indices. */
std::unordered_map<std::string, uint32_t> input_map_;
/*! \brief Map of output names to output indices. */
std::unordered_map<std::string, uint32_t> output_map_;
/*! \brief Used for quick node input DLTensor* lookup given an input eid. */
std::vector<std::vector<DLTensor*>> input_dltensors_;
/*! \brief Used for quick node output DLTensor* lookup given an output eid. */
std::vector<std::vector<DLTensor*>> output_dltensors_;
/*! \brief Used for quick node(both model output and op input) DLTensor* lookup given an eid. */
std::vector<std::vector<DLTensor*>> both_output_opinput_dltensors_;
/*! \brief Used for quick entry indexing. */
std::vector<uint32_t> node_row_ptr_;
/*! \brief Output entries. */
std::vector<NodeEntry> outputs_;
/*! \brief Additional graph attributes. */
GraphAttr attrs_;
/*! \brief The code module that contains both host and device code. */
tvm::runtime::Module module_;
/*! \brief Execution context of all devices including the host. */
std::vector<Device> devices_;
/*! \brief Common storage pool for all devices. */
std::vector<NDArray> storage_pool_;
/*! \brief Data entry of each node. */
std::vector<NDArray> data_entry_;
/*! \brief Data alignment of each node. */
std::vector<size_t> data_alignment_;
/*! \brief Operator on each node. */
std::vector<std::function<void()>> op_execs_;
/*! \brief Linked parameter lookup function. */
PackedFunc lookup_linked_param_;
/*! \brief Module's _lookup_linked_param function, used by DefaultLookupLinkedParam. */
PackedFunc module_lookup_linked_param_;
/*!
* \brief True when module_lookup_linked_param_ is valid.
* When the module does not include linked parmeters, module_lookup_linked_param_ will be nullptr.
*/
bool module_lookup_linked_param_valid_;
};
std::vector<Device> GetAllDevice(const TVMArgs& args, int dev_start_arg);
} // namespace runtime
} // namespace tvm
#endif // TVM_RUNTIME_GRAPH_EXECUTOR_GRAPH_EXECUTOR_H_