From: Efimov Alexander/AI Tools Lab/./Samsung Electronics Date: Thu, 2 Aug 2018 17:04:30 +0000 (+0300) Subject: Soft backend: split generator file (#842) X-Git-Tag: nncc_backup~2267 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e08664bdb89ff28cba02f48c6824be5eadd186bf;p=platform%2Fcore%2Fml%2Fnnfw.git Soft backend: split generator file (#842) Add library with common support functions for operations Signed-off-by: Efimov Alexander --- diff --git a/contrib/nnc/libs/backend/soft/CMakeLists.txt b/contrib/nnc/libs/backend/soft/CMakeLists.txt index 4dca6e7..f3225a2 100644 --- a/contrib/nnc/libs/backend/soft/CMakeLists.txt +++ b/contrib/nnc/libs/backend/soft/CMakeLists.txt @@ -1,6 +1,6 @@ -set(SOFT_BACKEND_COMMON_SOURCES src/soft_backend.cpp src/generator.cpp src/model_analyzer.cpp) -set(SOFT_BACKEND_CPP_SOURCES src/cpp_backend.cpp) -set(SOFT_BACKEND_C_SOURCES src/c_backend.cpp) +set(SOFT_BACKEND_COMMON_SOURCES src/soft_backend.cpp src/base_generator.cpp src/model_analyzer.cpp) +set(SOFT_BACKEND_CPP_SOURCES src/cpp_backend.cpp src/cpp_generator.cpp) +set(SOFT_BACKEND_C_SOURCES src/c_backend.cpp src/c_generator.cpp) set(DEF_CONV src/def2src.cpp) file(GLOB_RECURSE TESTS "src/*.test.cpp") @@ -44,7 +44,7 @@ endfunction(make_soft_backend) make_soft_backend(soft_backend_cpp ${SOFT_BACKEND_CPP_SOURCES}) make_soft_backend(soft_backend_c ${SOFT_BACKEND_C_SOURCES}) -add_nncc_test(nnc_soft_backend_test ${TESTS} ${SOFT_BACKEND_COMMON_SOURCES}) +add_nncc_test(nnc_soft_backend_test ${TESTS} ${SOFT_BACKEND_COMMON_SOURCES} ${SOFT_BACKEND_CPP_SOURCES}) nncc_target_link_libraries(nnc_soft_backend_test nncc_core nnc_core nnc_plugin_core nncc_foundation soft_backend_common) target_include_directories(nnc_soft_backend_test PUBLIC include) target_include_directories(nnc_soft_backend_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/contrib/nnc/libs/backend/soft/include/base_generator.h b/contrib/nnc/libs/backend/soft/include/base_generator.h new file mode 100644 index 0000000..134160b --- /dev/null +++ b/contrib/nnc/libs/backend/soft/include/base_generator.h @@ -0,0 +1,54 @@ +#ifndef _NNC_SOFT_BACKEND_BASE_GENERATOR_H_ +#define _NNC_SOFT_BACKEND_BASE_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); + +protected: + virtual void formatTensorNames(const ModelAnalyzer &ma) = 0; + 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); + + 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); + + std::vector _formattedTensors; + + // Code output file + std::string _headerFile; + // Code output file + std::string _codeFile; + // Model output file + std::string _modelFile; +}; + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + +#endif //_NNC_SOFT_BACKEND_BASE_GENERATOR_H_ diff --git a/contrib/nnc/libs/backend/soft/include/c_generator.h b/contrib/nnc/libs/backend/soft/include/c_generator.h new file mode 100644 index 0000000..5ccfdea --- /dev/null +++ b/contrib/nnc/libs/backend/soft/include/c_generator.h @@ -0,0 +1,40 @@ +#ifndef _NNC_SOFT_BACKEND_C_GENERATOR_H_ +#define _NNC_SOFT_BACKEND_C_GENERATOR_H_ + +#include "base_generator.h" + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +// 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); + +protected: + void formatTensorNames(const ModelAnalyzer &ma) override; + 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_C_GENERATOR_H_ diff --git a/contrib/nnc/libs/backend/soft/include/cpp_generator.h b/contrib/nnc/libs/backend/soft/include/cpp_generator.h new file mode 100644 index 0000000..523d8ac --- /dev/null +++ b/contrib/nnc/libs/backend/soft/include/cpp_generator.h @@ -0,0 +1,45 @@ +#ifndef _NNC_SOFT_BACKEND_CPP_GENERATOR_H_ +#define _NNC_SOFT_BACKEND_CPP_GENERATOR_H_ + +#include "base_generator.h" + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +// 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); + +protected: + void formatTensorNames(const ModelAnalyzer &ma) override; + void materializeHeader(std::ostream &out, const ModelAnalyzer &ma) override; + + void gatherOperationArguments(const ModelAnalyzer &ma, + const std::vector &argIds, + std::vector &args); + void materializeInferenceSequence(std::ostream &out, const ModelAnalyzer &ma); + void materializeCode(std::ostream &out, const ModelAnalyzer &ma) override; +}; + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + +#endif //_NNC_SOFT_BACKEND_CPP_GENERATOR_H_ diff --git a/contrib/nnc/libs/backend/soft/include/generator.h b/contrib/nnc/libs/backend/soft/include/generator.h deleted file mode 100644 index a87733e..0000000 --- a/contrib/nnc/libs/backend/soft/include/generator.h +++ /dev/null @@ -1,99 +0,0 @@ -#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); - -protected: - virtual void formatTensorNames(const ModelAnalyzer &ma) = 0; - 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); - - 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); - - std::vector _formattedTensors; - - // 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); - -protected: - void formatTensorNames(const ModelAnalyzer &ma) override; - void materializeHeader(std::ostream &out, const ModelAnalyzer &ma) override; - - void gatherOperationArguments(const ModelAnalyzer &ma, - const std::vector &argIds, - std::vector &args); - void materializeInferenceSequence(std::ostream &out, const ModelAnalyzer &ma); - 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); - -protected: - void formatTensorNames(const ModelAnalyzer &ma) override; - 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/src/base_generator.cpp b/contrib/nnc/libs/backend/soft/src/base_generator.cpp new file mode 100644 index 0000000..e911a3e --- /dev/null +++ b/contrib/nnc/libs/backend/soft/src/base_generator.cpp @@ -0,0 +1,172 @@ +#include "base_generator.h" +#include "model_analyzer.h" +#include "PluginException.h" +#include "nnc/core/IR/model/actions/ShapeInference.h" + +#include +#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 parameters_format +{ +const int MAGIC_LEN = 4; +const int VERSION_LEN = 4; +const int HASH_LEN = 4; +const int HEADER_LEN = MAGIC_LEN + VERSION_LEN + HASH_LEN; + +const char MAGIC[MAGIC_LEN + 1] = "NNMP"; // Neural Network Model Parameters +} + +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)) + { + if (errno == ENOENT) + { + // file not found, try to create it + ofstream f(path); + if (!f.good()) + throw PluginException("Can not create output file: " + path); + // try again + int res = stat(path.c_str(), &s); + (void) res; + // on this step everythink should be fine, since we just created file + assert(!res); + return true; + } + else + throw PluginException("Can not get info for file: " + path); + } + // check that selected file is regular or char device(to accept /dev/null, and simular) + if (!(s.st_mode & (S_IFREG | S_IFCHR))) + throw PluginException("Not a regular file: " + path); + return true; +} + +using ostream_ptr = unique_ptr; + +ostream_ptr getStream(const string &path) +{ + if (path.empty()) + return ostream_ptr(&cout, [](ostream *){}); + ofstream *ofs = new ofstream(path); + if (ofs->fail()) + { + delete ofs; + throw PluginException("Can not open code output file: " + path); + } + return ostream_ptr(ofs, [](ostream *ofs){delete ofs;}); +} + +} // 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 checkHeader = fillFileStats(_headerFile, headerStat); + bool checkCode = fillFileStats(_codeFile, codeStat); + bool checkModel = fillFileStats(_modelFile, modelStat); + + if ((checkHeader && checkCode && areFilesEqual(headerStat, codeStat)) || + (checkHeader && checkModel && areFilesEqual(headerStat, modelStat)) || + (checkCode && checkModel && areFilesEqual(codeStat, modelStat))) + { + throw PluginException("output files should not be equal"); + } +} + +void BaseCodeGenerator::materializeModelParams(ostream &out, const ModelAnalyzer &ma) +{ + using namespace parameters_format; + + // First form a dump header + char header[HEADER_LEN]; + uint32_t version = ma.getFormatVersion(); + uint32_t hash = ma.getModelHash(); + static_assert(VERSION_LEN == sizeof(version), "version length mismatch"); + static_assert(HASH_LEN == sizeof(hash), "hash length mismatch"); + memcpy(header, MAGIC, MAGIC_LEN); + memcpy(header + MAGIC_LEN, &version, VERSION_LEN); + memcpy(header + MAGIC_LEN + VERSION_LEN, &hash, HASH_LEN); + + out.write(header, HEADER_LEN); + if (out.fail()) + { + throw PluginException("Failed to write model parameters header"); + } + auto ¶ms = ma.getPackedParameters(); + out.write(params.data(), params.size()); + if (out.fail()) + { + throw PluginException("Failed to write model Parameters"); + } +} + +void BaseCodeGenerator::generate(Graph *g) +{ + // inference shapes + core::IR::model::ShapeInference si; + g->accept(&si); + // visit and analyze graph + ModelAnalyzer ma; + g->accept(&ma); + // rename tensors for specific backend language + formatTensorNames(ma); + // Print header + auto headerStream = getStream(_headerFile); + materializeHeader(*headerStream, ma); + headerStream.reset(); + + // Print code + auto codeStream = getStream(_codeFile); + materializeCode(*codeStream, ma); + codeStream.reset(); + + // Print model parameters + auto modelStream = getStream(_modelFile); + materializeModelParams(*modelStream, ma); + modelStream.reset(); +} + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + diff --git a/contrib/nnc/libs/backend/soft/src/c_backend.cpp b/contrib/nnc/libs/backend/soft/src/c_backend.cpp index 73c3178..173799e 100644 --- a/contrib/nnc/libs/backend/soft/src/c_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/c_backend.cpp @@ -1,5 +1,5 @@ #include "soft_backend.h" -#include "generator.h" +#include "c_generator.h" #include "ConfigException.h" diff --git a/contrib/nnc/libs/backend/soft/src/c_generator.cpp b/contrib/nnc/libs/backend/soft/src/c_generator.cpp new file mode 100644 index 0000000..bb07423 --- /dev/null +++ b/contrib/nnc/libs/backend/soft/src/c_generator.cpp @@ -0,0 +1,45 @@ +#include "c_generator.h" +#include "model_analyzer.h" + +using namespace std; +using namespace nncc::contrib; +using namespace nncc::contrib::core::IR::model; + +namespace nncc +{ +namespace contrib +{ +namespace backend +{ +namespace soft +{ + +CCodeGenerator CCodeGenerator::create(const string &headerFile, + const string &codeFile, + const string &modelFile) +{ + CCodeGenerator gen(headerFile, codeFile, modelFile); + gen.checkCorrectness(); + return gen; +} + +void CCodeGenerator::formatTensorNames(const ModelAnalyzer &ma) +{ + // TODO format tensor names according to c backend requirements +} + +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 +} + +} // namespace soft +} // namespace backend +} // namespace contrib +} // namespace nncc + diff --git a/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp b/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp index 2f5f9e6..a0a3ae4 100644 --- a/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/cpp_backend.cpp @@ -1,5 +1,5 @@ #include "soft_backend.h" -#include "generator.h" +#include "cpp_generator.h" #include "ConfigException.h" diff --git a/contrib/nnc/libs/backend/soft/src/generator.cpp b/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp similarity index 62% rename from contrib/nnc/libs/backend/soft/src/generator.cpp rename to contrib/nnc/libs/backend/soft/src/cpp_generator.cpp index 9ce0780..630c05c 100644 --- a/contrib/nnc/libs/backend/soft/src/generator.cpp +++ b/contrib/nnc/libs/backend/soft/src/cpp_generator.cpp @@ -1,18 +1,6 @@ -#include "generator.h" +#include "cpp_generator.h" #include "model_analyzer.h" #include "PluginException.h" -#include "nnc/core/IR/model/actions/ShapeInference.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include #include "param_constants.def" @@ -44,159 +32,6 @@ 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)) - { - if (errno == ENOENT) - { - // file not found, try to create it - ofstream f(path); - if (!f.good()) - throw PluginException("Can not create output file: " + path); - // try again - int res = stat(path.c_str(), &s); - (void) res; - // on this step everythink should be fine, since we just created file - assert(!res); - return true; - } - else - throw PluginException("Can not get info for file: " + path); - } - // check that selected file is regular or char device(to accept /dev/null, and simular) - if (!(s.st_mode & (S_IFREG | S_IFCHR))) - throw PluginException("Not a regular file: " + path); - return true; -} - -using ostream_ptr = unique_ptr; - -ostream_ptr getStream(const string &path) -{ - if (path.empty()) - return ostream_ptr(&cout, [](ostream *){}); - ofstream *ofs = new ofstream(path); - if (ofs->fail()) - { - delete ofs; - throw PluginException("Can not open code output file: " + path); - } - return ostream_ptr(ofs, [](ostream *ofs){delete ofs;}); -} - -} // 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 checkHeader = fillFileStats(_headerFile, headerStat); - bool checkCode = fillFileStats(_codeFile, codeStat); - bool checkModel = fillFileStats(_modelFile, modelStat); - - if ((checkHeader && checkCode && areFilesEqual(headerStat, codeStat)) || - (checkHeader && checkModel && areFilesEqual(headerStat, modelStat)) || - (checkCode && checkModel && areFilesEqual(codeStat, modelStat))) - { - throw PluginException("output files should not be equal"); - } -} - -void BaseCodeGenerator::materializeModelParams(ostream &out, const ModelAnalyzer &ma) -{ - using namespace params; - - // First form a dump header - char header[HEADER_LEN]; - uint32_t version = ma.getFormatVersion(); - uint32_t hash = ma.getModelHash(); - static_assert(VERSION_LEN == sizeof(version), "version length mismatch"); - static_assert(HASH_LEN == sizeof(hash), "hash length mismatch"); - memcpy(header, MAGIC, MAGIC_LEN); - memcpy(header + MAGIC_LEN, &version, VERSION_LEN); - memcpy(header + MAGIC_LEN + VERSION_LEN, &hash, HASH_LEN); - - out.write(header, HEADER_LEN); - if (out.fail()) - { - throw PluginException("Failed to write model parameters header"); - } - auto ¶msVec = ma.getPackedParameters(); - out.write(paramsVec.data(), paramsVec.size()); - if (out.fail()) - { - throw PluginException("Failed to write model Parameters"); - } -} - -void BaseCodeGenerator::generate(Graph *g) -{ - // inference shapes - core::IR::model::ShapeInference si; - g->accept(&si); - // visit and analyze graph - ModelAnalyzer ma; - g->accept(&ma); - // rename tensors for specific backend language - formatTensorNames(ma); - // Print header - auto headerStream = getStream(_headerFile); - materializeHeader(*headerStream, ma); - headerStream.reset(); - - // Print code - auto codeStream = getStream(_codeFile); - materializeCode(*codeStream, ma); - codeStream.reset(); - - // Print model parameters - auto modelStream = getStream(_modelFile); - materializeModelParams(*modelStream, ma); - modelStream.reset(); -} - -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::formatTensorNames(const ModelAnalyzer &ma) -{ - // TODO format tensor names according to c backend requirements -} - -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) @@ -232,7 +67,7 @@ void CPPCodeGenerator::formatTensorNames(const ModelAnalyzer &ma) } } } - _formattedTensors.push_back(std::move(formattedName)); + _formattedTensors.push_back(move(formattedName)); } } diff --git a/contrib/nnc/libs/backend/soft/src/generator.test.cpp b/contrib/nnc/libs/backend/soft/src/generator.test.cpp index ba5c546..0aeb150 100644 --- a/contrib/nnc/libs/backend/soft/src/generator.test.cpp +++ b/contrib/nnc/libs/backend/soft/src/generator.test.cpp @@ -1,4 +1,5 @@ -#include "generator.h" +#include "cpp_generator.h" + #include "PluginException.h" #include diff --git a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp index 18ed127..381ddb0 100644 --- a/contrib/nnc/libs/backend/soft/src/soft_backend.cpp +++ b/contrib/nnc/libs/backend/soft/src/soft_backend.cpp @@ -8,7 +8,8 @@ #include "ConfigException.h" #include "PluginType.h" #include "nnc/core/IR/model/graph/graph.h" -#include "generator.h" +#include "cpp_generator.h" +#include "c_generator.h" using namespace std; using namespace nncc::contrib;