From 3a0d76dbde1f7f71b85942f49e4a07a84388462d Mon Sep 17 00:00:00 2001 From: "Efimov Alexander/AI Tools Lab/./Samsung Electronics" Date: Fri, 6 Jul 2018 14:22:01 +0300 Subject: [PATCH] Soft backend C/C++ framework (#483) Introduce generator framework Main generator and model analyzer structure Signed-off-by: Efimov Alexander --- contrib/nnc/libs/backend/soft/CMakeLists.txt | 2 +- contrib/nnc/libs/backend/soft/include/generator.h | 89 +++++++++++++++ .../nnc/libs/backend/soft/include/model_analyzer.h | 83 ++++++++++++++ contrib/nnc/libs/backend/soft/src/generator.cpp | 123 +++++++++++++++++++++ .../nnc/libs/backend/soft/src/model_analyzer.cpp | 70 ++++++++++++ contrib/nnc/libs/backend/soft/src/soft_backend.cpp | 22 +++- 6 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 contrib/nnc/libs/backend/soft/include/generator.h create mode 100644 contrib/nnc/libs/backend/soft/include/model_analyzer.h create mode 100644 contrib/nnc/libs/backend/soft/src/generator.cpp create mode 100644 contrib/nnc/libs/backend/soft/src/model_analyzer.cpp diff --git a/contrib/nnc/libs/backend/soft/CMakeLists.txt b/contrib/nnc/libs/backend/soft/CMakeLists.txt index ad14331..4ddad1f 100644 --- a/contrib/nnc/libs/backend/soft/CMakeLists.txt +++ b/contrib/nnc/libs/backend/soft/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LANG_BACKEND_SOURCES src/soft_backend.cpp) +file(GLOB_RECURSE LANG_BACKEND_SOURCES src/*.cpp) add_library(soft_backend SHARED ${LANG_BACKEND_SOURCES}) diff --git a/contrib/nnc/libs/backend/soft/include/generator.h b/contrib/nnc/libs/backend/soft/include/generator.h new file mode 100644 index 0000000..40f79b2 --- /dev/null +++ b/contrib/nnc/libs/backend/soft/include/generator.h @@ -0,0 +1,89 @@ +#ifndef _NNC_SOFT_BACKEND_GENERATOR_H_ +#define _NNC_SOFT_BACKEND_GENERATOR_H_ + +#include "nnc/core/IR/model/graph/graph.h" +#include +#include + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +class ModelAnalyzer; + +class BaseCodeGenerator +{ +public: + + void generate(nncc::contrib::core::IR::model::Graph *g); + + virtual void materializeHeader(std::ostream &out, const ModelAnalyzer &ma) = 0; + virtual void materializeCode(std::ostream &out, const ModelAnalyzer &ma) = 0; + void materializeModelParams(std::ostream &out, const ModelAnalyzer &ma); + +protected: + BaseCodeGenerator(BaseCodeGenerator &g) = default; + + // check validity of selected output files, throws appropriate exception on error + void checkCorrectness(); + + BaseCodeGenerator(const std::string &headerFile, + const std::string &codeFile, const std::string &modelFile); + + // Code output file + std::string _headerFile; + // Code output file + std::string _codeFile; + // Model output file + std::string _modelFile; +}; + +// C++ generator +class CPPCodeGenerator: public BaseCodeGenerator +{ + CPPCodeGenerator(const std::string &headerFile, const std::string &codeFile, const std::string &modelFile): + BaseCodeGenerator(headerFile, codeFile, modelFile) + { + // EMPTY + } + +public: + + static CPPCodeGenerator create(const std::string &headerFile, + const std::string &codeFile, + const std::string &modelFile); + + void materializeHeader(std::ostream &out, const ModelAnalyzer &ma) override; + void materializeCode(std::ostream &out, const ModelAnalyzer &ma) override; +}; + +// C generator +class CCodeGenerator: public BaseCodeGenerator +{ + CCodeGenerator(const std::string &headerFile, const std::string &codeFile, const std::string &modelFile): + BaseCodeGenerator(headerFile, codeFile, modelFile) + { + // EMPTY + } + +public: + + static CCodeGenerator create(const std::string &headerFile, + const std::string &codeFile, + const std::string &modelFile); + + void materializeHeader(std::ostream &out, const ModelAnalyzer &ma) override; + void materializeCode(std::ostream &out, const ModelAnalyzer &ma) override; +}; + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + +#endif //_NNC_SOFT_BACKEND_GENERATOR_H_ diff --git a/contrib/nnc/libs/backend/soft/include/model_analyzer.h b/contrib/nnc/libs/backend/soft/include/model_analyzer.h new file mode 100644 index 0000000..0ea03d0 --- /dev/null +++ b/contrib/nnc/libs/backend/soft/include/model_analyzer.h @@ -0,0 +1,83 @@ +#ifndef _NNC_SOFT_BACKEND_MODEL_ANALYZER_H_ +#define _NNC_SOFT_BACKEND_MODEL_ANALYZER_H_ + +#include "nnc/core/IR/model/visitor/visitor.h" + +#include +#include +#include + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +namespace ADT = nncc::contrib::core::IR::model::ADT; +namespace ops = nncc::contrib::core::IR::model::ops; + +class ModelAnalyzer: public nncc::contrib::core::IR::model::Visitor +{ +public: + void visit(ADT::INode *node, ops::ConcatOp &op) override; + void visit(ADT::INode *node, ops::Conv2DOp &op) override; + void visit(ADT::INode *node, ops::DepthwiseConv2DOp &op) override; + void visit(ADT::INode *node, ops::SoftmaxOp &op) override; + void visit(ADT::INode *node, ops::PoolOp &op) override; + void visit(ADT::INode *node, ops::FullyConnectedOp &op) override; + void visit(ADT::INode *node, ops::CappedReluOp &op) override; + void visit(ADT::INode *node, ops::BiasAddOp &op) override; + void visit(ADT::INode *node, ops::VariableOp &op) override; + void visit(ADT::INode *node, ops::ReluOp &op) override; + void visit(ADT::INode *node, ops::ReshapeOp &op) override; + + // stub for operation description + struct OpDescr + { + // TODO add information about operation, inputs, outputs, place model data stored + }; + + const std::vector &getInputs() const + { + // TODO return list of input tensor names + // UB here, suppress warnings + return *static_cast*>(nullptr); + } + + const std::vector &getOutputs() const + { + // TODO return list of output tensor names + // UB here, suppress warnings + return *static_cast*>(nullptr); + } + + const std::vector &getInferenceSequence() const + { + // TODO return sequence of operations + // UB here, suppress warnings + return *static_cast*>(nullptr); + } + + const std::vector &getPackedParameters() const + { + // TODO return array containing compressed parameters for operations + // UB here, suppress warnings + return *static_cast*>(nullptr); + } + + uint64_t getFormatVersion() const + { + // TODO return analyzer version, part of model parameters file. + return 0; + } +}; + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + +#endif //_NNC_SOFT_BACKEND_MODEL_ANALYZER_H_ diff --git a/contrib/nnc/libs/backend/soft/src/generator.cpp b/contrib/nnc/libs/backend/soft/src/generator.cpp new file mode 100644 index 0000000..2890d54 --- /dev/null +++ b/contrib/nnc/libs/backend/soft/src/generator.cpp @@ -0,0 +1,123 @@ +#include "generator.h" +#include "model_analyzer.h" +#include "PluginException.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace nncc::contrib; +using namespace nncc::contrib::core::IR::model; + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +namespace +{ + +bool areFilesEqual(const struct stat &f1, const struct stat &f2) +{ + return f1.st_dev == f2.st_dev && f1.st_ino == f2.st_ino; +} + +bool fillFileStats(const string &path, struct stat &s) +{ + if (path.empty()) + return false; + if (stat(path.c_str(), &s)) + throw PluginException("Can not get info for file: " + path); + return true; +} + +} // unnamed namespace + +BaseCodeGenerator::BaseCodeGenerator(const string &headerFile, const string &codeFile, const string &modelFile): + _headerFile(headerFile), _codeFile(codeFile), _modelFile(modelFile) +{ + // EMPTY +} + +void BaseCodeGenerator::checkCorrectness() +{ + // check that header, code and model file paths refers to different files. + // Exception: stdout could be common output stream + struct stat headerStat, codeStat, modelStat; + bool skipHeader = fillFileStats(_headerFile, headerStat); + bool skipCode = fillFileStats(_codeFile, codeStat); + bool skipModel = fillFileStats(_modelFile, modelStat); + + if ((!skipHeader && !skipCode && areFilesEqual(headerStat, codeStat)) || + (!skipHeader && !skipModel && areFilesEqual(headerStat, modelStat)) || + (!skipCode && !skipModel && areFilesEqual(codeStat, modelStat))) + { + throw PluginException("output files should not be equal"); + } +} + +void BaseCodeGenerator::materializeModelParams(ostream &out, const ModelAnalyzer &ma) +{ + // TODO dump compressed model data +} + +void BaseCodeGenerator::generate(Graph *g) +{ + // visit and analyze graph + ModelAnalyzer ma; + g->accept(&ma); + // materialize code +} + +CCodeGenerator CCodeGenerator::create(const std::string &headerFile, + const std::string &codeFile, + const std::string &modelFile) +{ + CCodeGenerator gen(headerFile, codeFile, modelFile); + gen.checkCorrectness(); + return gen; +} + +void CCodeGenerator::materializeHeader(ostream &out, const ModelAnalyzer &ma) +{ + // TODO emit C header to out stream +} + +void CCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma) +{ + // TODO emit C code to out stream +} + +CPPCodeGenerator CPPCodeGenerator::create(const std::string &headerFile, + const std::string &codeFile, + const std::string &modelFile) +{ + CPPCodeGenerator gen(headerFile, codeFile, modelFile); + gen.checkCorrectness(); + return gen; +} + +void CPPCodeGenerator::materializeHeader(ostream &out, const ModelAnalyzer &ma) +{ + // TODO emit C++ header +} + +void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma) +{ + // TODO emit C++ code to out stream +} + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + diff --git a/contrib/nnc/libs/backend/soft/src/model_analyzer.cpp b/contrib/nnc/libs/backend/soft/src/model_analyzer.cpp new file mode 100644 index 0000000..1670fbd --- /dev/null +++ b/contrib/nnc/libs/backend/soft/src/model_analyzer.cpp @@ -0,0 +1,70 @@ +#include "model_analyzer.h" + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +void ModelAnalyzer::visit(ADT::INode *node, ops::ConcatOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::Conv2DOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::DepthwiseConv2DOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::SoftmaxOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::PoolOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::FullyConnectedOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::CappedReluOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::BiasAddOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::VariableOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::ReluOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +void ModelAnalyzer::visit(ADT::INode *node, ops::ReshapeOp &op) +{ + // TODO fill appropriate fields in operations sequence and parameters +} + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc diff --git a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp index 7479624..d2fc150 100644 --- a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp @@ -7,11 +7,15 @@ #include "PluginException.h" #include "ConfigException.h" #include "PluginType.h" +#include "nnc/core/IR/model/graph/graph.h" +#include "generator.h" using namespace std; using namespace nncc::contrib; using namespace nncc::contrib::config; using namespace nncc::contrib::plugin; +using namespace nncc::contrib::core::IR::model; +using namespace nncc::contrib::backend::soft; namespace { @@ -110,13 +114,27 @@ void SoftBackendInstance::fillSession() void SoftBackendInstance::checkConfig() { assert(_target != LANG_ID::INVALID && "No target language selected"); + // Nothing to check for now } void *SoftBackendInstance::execute(void *data) { assert(_target != LANG_ID::INVALID); - // TODO Implementation here - cout << "\n[" << pluginName << "] Plugin executed" << endl; + assert(data); + Graph *graph = static_cast(data); + switch (_target) + { + case LANG_ID::C: + CCodeGenerator::create(_outHeaderFile, _outCodeFile, _outModelFile).generate(graph); + break; + case LANG_ID::CPP: + CPPCodeGenerator::create(_outHeaderFile, _outCodeFile, _outModelFile).generate(graph); + break; + default: + assert(false && "Unsupported language"); + break; + }; + clog << "[" << pluginName << "] Plugin executed" << endl; return data; } -- 2.7.4