#include "nnc/core/linalg/TensorVariant.h"
#include <vector>
+#include <list>
+#include <map>
#include <utility>
#include <string>
#include <cassert>
+#include <limits>
namespace nncc
{
namespace ADT = nncc::contrib::core::IR::model::ADT;
namespace ops = nncc::contrib::core::IR::model::ops;
+const size_t INVALID_TENSOR_ID = std::numeric_limits<size_t>::max();
+
class ModelAnalyzer: public nncc::contrib::core::IR::model::Visitor
{
public:
struct TensorDescription
{
std::string _name;
- bool _isNNOutput; // true if is it NN output tensor
+ bool _isNNInput; // true if this is NN input tensor
+ bool _isNNOutput; // true if this is NN output tensor
};
// operation description
Type _type;
ADT::INode *_node;
std::string _opName;
+ // list of input tensors
+ std::vector<size_t> _inputs;
// list of output tensors
- std::vector<TensorDescription> _outputs;
+ std::vector<size_t> _outputs;
size_t _paramStartOffset;
};
- const std::vector<std::string> &getInputs() const
+ const std::vector<size_t> &getInputs() const
{
return _inputs;
}
- const std::vector<std::string> &getOutputs() const
+ const std::vector<size_t> &getOutputs() const
{
return _outputs;
}
- const std::vector<OpDescr> &getInferenceSequence() const
+ const std::vector<TensorDescription> &getTensors() const
+ {
+ return _tensors;
+ }
+
+ const std::list<OpDescr> &getInferenceSequence() const
{
return _inferenceSequence;
}
private:
void addOpDescr(ADT::INode *node, const std::string &name);
+ size_t allocateTensor(const std::string &name = std::string(),
+ bool isNNInput = false, bool isNNOutput = false);
void packData(const void *data, size_t size);
template <typename T>
const uint32_t _formatVersion = 1;
uint32_t _modelHash = 0;
std::vector<char> _packedParameters;
- std::vector<OpDescr> _inferenceSequence;
- size_t _localTensorsN = 0;
- std::vector<std::string> _inputs;
- std::vector<std::string> _outputs;
+ std::list<OpDescr> _inferenceSequence;
+ size_t _allocatedTensors = 0;
+ std::vector<size_t> _inputs;
+ std::vector<size_t> _outputs;
+ std::vector<TensorDescription> _tensors;
+ std::map<const ADT::INode *, OpDescr *> _nodeToDescr;
};
} // namespace soft
"public:\n"
" " << className << "(const std::string ¶metersPath);\n";
// generate input setters
- for (const string &inName: ma.getInputs())
- out << " void set_" << inName << "(const Tensor& t);\n";
+ for (const size_t inId: ma.getInputs())
+ {
+ const string &tName = ma.getTensors()[inId]._name;
+ out << " void set" << tName << "(const Tensor& t);\n";
+ }
// generate output getters
- for (const string &outName: ma.getOutputs())
- out << " std::shared_ptr<Tensor> get_" << outName << "();\n";
+ for (const size_t outId: ma.getOutputs())
+ {
+ const string &tName = ma.getTensors()[outId]._name;
+ out << " std::shared_ptr<Tensor> get" << tName << "();\n";
+ }
out << " void doInference();\n\n"
"private:\n"
" " << className << "() = delete;\n"
- " " << className << "(const " << className << " &orig) = delete;"
+ " " << className << "(const " << className << " &orig) = delete;\n"
" " << className << " &operator=(const " << className << " &orig) = delete;\n";
// generate input/output tensors
- for (const string &inName: ma.getInputs())
- out << " Tensor _" << inName << ";\n";
- for (const string &outName: ma.getOutputs())
- out << " std::shared_ptr<Tensor> _" << outName << ";\n";
- out << "};\n";
-}
-
-// print allocation of temporary tensors
-static void printTmpTensors(ostream &out, const ModelAnalyzer::OpDescr &op)
-{
- for (const ModelAnalyzer::TensorDescription &td: op._outputs)
+ for (const size_t inId: ma.getInputs())
{
- if (td._isNNOutput)
- continue;
- out << " Tensor " << td._name << ";\n";
+ const string &tName = ma.getTensors()[inId]._name;
+ out << " Tensor " << tName << ";\n";
}
-}
-
-// generate function output arguments
-static void gatherOperationCallOutputs(const ModelAnalyzer::OpDescr &op, vector<string> &args)
-{
- for (const ModelAnalyzer::TensorDescription &td: op._outputs)
+ for (const size_t outId: ma.getOutputs())
{
- const string &tensorName = td._name;
- if (td._isNNOutput)
- args.push_back("*" + tensorName);
- else
- args.push_back(tensorName);
+ const string &tName = ma.getTensors()[outId]._name;
+ out << " std::shared_ptr<Tensor> " << tName << ";\n";
}
+ out << "};\n";
}
-// generate function input arguments
-static void gatherOperationCallInputs(const ModelAnalyzer::OpDescr &op, map<INode*,
- const ModelAnalyzer::OpDescr *> &node2Descr, vector<string> &args)
+// print allocation of temporary tensors
+static void printTmpTensors(ostream &out, const ModelAnalyzer &ma, const ModelAnalyzer::OpDescr &op)
{
- for (const INode::IODescriptor &d: op._node->getPrevNodes())
+ for (size_t id: op._outputs)
{
- size_t idx = d.index;
- INode *node = d.node;
- assert(node2Descr.find(node) != node2Descr.end());
- const ModelAnalyzer::OpDescr &descr = *node2Descr[node];
- const ModelAnalyzer::TensorDescription &tDescr = descr._outputs[idx];
- const string &tensorName = tDescr._name;
- if (tDescr._isNNOutput)
- args.push_back("*" + tensorName);
- else
- args.push_back(tensorName);
+ const ModelAnalyzer::TensorDescription &td = ma.getTensors()[id];
+ assert(!td._isNNInput && "no input nodes should be inserted into inference sequence");
+ if (td._isNNOutput)
+ continue;
+ const string &tName = ma.getTensors()[id]._name;
+ out << " Tensor " << tName << ";\n";
}
}
}
}
+// gather function arguments from vector of tensor descriptions
+void CPPCodeGenerator::gatherOperationArguments(const ModelAnalyzer &ma,
+ const vector<size_t> &argIds,
+ vector<string> &args)
+{
+ for (size_t id: argIds)
+ {
+ const string &tensorName = ma.getTensors()[id]._name;
+ if (ma.getTensors()[id]._isNNOutput)
+ {
+ args.push_back("*" + tensorName);
+ }
+ else
+ {
+ args.push_back(tensorName);
+ }
+ }
+}
+
// generate inference sequence
-static void materializeCPPInferenceSequence(ostream &out, const ModelAnalyzer &ma)
+void CPPCodeGenerator::materializeInferenceSequence(ostream &out, const ModelAnalyzer &ma)
{
using OpDescr = ModelAnalyzer::OpDescr;
- map<INode*, const OpDescr *> node2Descr;
for (const ModelAnalyzer::OpDescr &op: ma.getInferenceSequence())
{
- node2Descr.insert(pair<INode *, const OpDescr *>(op._node, &op));
using Type = OpDescr::Type;
using TensorDescription = ModelAnalyzer::TensorDescription;
if (op._type == Type::IN)
continue;
// create temporary tensors
- printTmpTensors(out, op);
+ printTmpTensors(out, ma, op);
// materialize call
out << " " << op._opName << "(";
const auto &prevNodes = op._node->getPrevNodes();
vector<string> args;
args.reserve(prevNodes.size() + outTensors.size() + 1);
// gather output arguments
- gatherOperationCallOutputs(op, args);
+ gatherOperationArguments(ma, op._outputs, args);
// parameters offset
args.push_back(to_string(op._paramStartOffset));
// gather input arguments
- gatherOperationCallInputs(op, node2Descr, args);
+ gatherOperationArguments(ma, op._inputs, args);
// put arguments into stream
printOperationArgs(out, args);
out << ");\n";
"}\n";
// gen input setters
- for (const string &inName: ma.getInputs())
+ for (size_t inId: ma.getInputs())
{
+ const string &inName = ma.getTensors()[inId]._name;
out << "void " << className << "::set_" << inName << "(const Tensor& t)\n"
"{\n"
" _" << inName << " = t;"
}
// gen output getters
- for (const string &outName: ma.getOutputs())
+ for (size_t outId: ma.getOutputs())
{
+ const string &outName = ma.getTensors()[outId]._name;
out << "shared_ptr<Tensor> " << className <<"::get_" << outName << "()\n"
"{\n"
" return _" << outName << ";"
}
out << "void " << className << "::doInference()\n"
"{\n";
- for (const string &outName: ma.getOutputs())
+ for (size_t outId: ma.getOutputs())
{
+ const string &outName = ma.getTensors()[outId]._name;
out << " _" << outName << ".reset(new Tensor());\n";
}
// gen inference sequence
- materializeCPPInferenceSequence(out, ma);
+ materializeInferenceSequence(out, ma);
out << "}";
}
{
size_t offset = _packedParameters.size();
OpDescr::Type type = OpDescr::Type::ORDINARY;
- vector<TensorDescription> outputs;
+ vector<size_t> nodeOutputs;
const std::string &name = node->getName();
+ size_t nodeTid = INVALID_TENSOR_ID;
if (node->getPrevNodes().empty())
{
- _inputs.push_back(name);
+ // process input node
+ nodeTid = allocateTensor(name, true);
+ _inputs.push_back(nodeTid);
type = OpDescr::Type::IN;
- } else
- if (!name.empty())
+ }
+ else if (!name.empty())
{
- _outputs.push_back(name);
+ // process output node
+ nodeTid = allocateTensor(name, false, true);
+ _outputs.push_back(nodeTid);
type = OpDescr::Type::OUT;
}
- if (type != OpDescr::Type::ORDINARY)
+ else
{
- outputs.push_back({"_" + name, type == OpDescr::Type::OUT});
- } else
+ // proces ordinary node
+ nodeTid = allocateTensor();
+ }
+ assert(nodeTid != INVALID_TENSOR_ID);
+ nodeOutputs.push_back(nodeTid);
+ // process node inputs
+ vector<size_t> nodeInputs;
+ for (const ADT::INode::IODescriptor &d: node->getPrevNodes())
{
- outputs.push_back({"tensor_" + to_string(_localTensorsN++), false});
+ size_t idx = d.index;
+ ADT::INode *node = d.node;
+ assert(_nodeToDescr.find(node) != _nodeToDescr.end());
+ const OpDescr &descr = *_nodeToDescr[node];
+ const size_t &inTid = descr._outputs[idx];
+ nodeInputs.push_back(inTid);
}
- _inferenceSequence.push_back({type, node, opName, std::move(outputs), offset});
+ _inferenceSequence.push_back({type, node, opName,
+ std::move(nodeInputs),
+ std::move(nodeOutputs),
+ offset});
+ _nodeToDescr[node] = &_inferenceSequence.back();
// TODO add model hashing
}
+size_t ModelAnalyzer::allocateTensor(const string &name, bool isNNInput, bool isNNOutput)
+{
+ assert(!(name.empty() && (isNNOutput || isNNInput)) && "Input or output tensor must have name");
+ size_t id = _allocatedTensors++;
+ _tensors.push_back({name, isNNInput, isNNOutput});
+ assert(_tensors.size() == _allocatedTensors);
+ return id;
+}
+
void ModelAnalyzer::visit(ADT::INode *node, ops::ConcatOp &op)
{
addOpDescr(node, "concat");