From: 박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 Date: Thu, 4 Oct 2018 07:00:24 +0000 (+0900) Subject: [enco] Emit global weight data as a binary file (#1741) X-Git-Tag: nncc_backup~1629 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0b8bc1d79a99c81733cc385e2321c728c0c4c814;p=platform%2Fcore%2Fml%2Fnnfw.git [enco] Emit global weight data as a binary file (#1741) With this commit, C++ code that enco generates no longer includes an initializer for global weight data. Instead, enco generates .bin file, and embed this data into an executable through corresponding asm file. This change significantly reduces the overhead of C++ code generation and its compilation. Signed-off-by: Jonghyun Park --- diff --git a/contrib/enco/core/src/AsmCode.cpp b/contrib/enco/core/src/AsmCode.cpp new file mode 100644 index 0000000..221cb34 --- /dev/null +++ b/contrib/enco/core/src/AsmCode.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 "AsmCode.h" + +namespace enco +{ + +void AsmCode::dump(std::ostream &os) const +{ + os << ".section .rodata" << std::endl; + os << ".global " << _varname << std::endl; + os << ".type " << _varname << ", @object" << std::endl; + os << ".align " << 4 << std::endl; + os << _varname << ":" << std::endl; + os << ".incbin " << '"' << _filename << '"' << std::endl; +} + +} // namespace enco diff --git a/contrib/enco/core/src/AsmCode.h b/contrib/enco/core/src/AsmCode.h new file mode 100644 index 0000000..c438928 --- /dev/null +++ b/contrib/enco/core/src/AsmCode.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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. + */ + +#ifndef __ENCO_ASM_CODE_H__ +#define __ENCO_ASM_CODE_H__ + +#include +#include + +namespace enco +{ + +class AsmCode +{ +public: + AsmCode(const std::string &filename, const std::string &varname) + : _filename{filename}, _varname{varname} + { + // DO NOTHING + } + +public: + void dump(std::ostream &) const; + +private: + std::string _filename; + std::string _varname; +}; + +} // namespace enco + +static inline std::ostream &operator<<(std::ostream &os, const enco::AsmCode &code) +{ + code.dump(os); + return os; +} + +#endif // __ENCO_ASM_CODE_H__ diff --git a/contrib/enco/core/src/Backend.cpp b/contrib/enco/core/src/Backend.cpp index 4270d3d..d436e98 100644 --- a/contrib/enco/core/src/Backend.cpp +++ b/contrib/enco/core/src/Backend.cpp @@ -19,6 +19,7 @@ #include "Session.h" #include "Code.h" +#include "AsmCode.h" #include "CppCode.h" #include "Transforms/Duplicate.h" @@ -129,12 +130,25 @@ void BackendImpl::compile(coco::Module *m, coco::Data *d) SplitPass split; split.runOnCode(code(sess)); - generate_global_data(code(sess)); + const std::string data_var = "data"; + const std::string data_filename = _prefix + ".bin"; + + // Generate 'bin' file + { + std::ofstream ofs{data_filename, std::ios::binary}; + generate_global_data(ofs, code(sess)); + } + + // Generate 'embed.S' file + { + std::ofstream ofs{_prefix + ".embed.S"}; + ofs << AsmCode{data_filename, data_var}; + } // TODO Run various transforms over enco::Code std::ofstream ofs{_prefix + ".cpp"}; - ofs << CppCode{code(sess)} << std::endl; + ofs << CppCode{data_var, code(sess)} << std::endl; } } // namespace enco diff --git a/contrib/enco/core/src/CppCode.cpp b/contrib/enco/core/src/CppCode.cpp index 512ce80..5ca0195 100644 --- a/contrib/enco/core/src/CppCode.cpp +++ b/contrib/enco/core/src/CppCode.cpp @@ -98,11 +98,7 @@ void CppCode::dump(std::ostream &os) const InvokeFunction invoke; pp::LinearDocument internal; - const std::string data_varname = "data"; - - auto data_exp = [&data_varname](const GlobalOffset &off) { - return pp::fmt(data_varname, " + ", off); - }; + auto data_exp = [this](const GlobalOffset &off) { return pp::fmt(_varname, " + ", off); }; // Record the subnet information std::map subnet_ctx; @@ -357,7 +353,7 @@ void CppCode::dump(std::ostream &os) const source.append(includes); source.append(); - source.append("uint8_t ", data_varname, "[] = { ", *enco::GlobalData::global(m), " };"); + source.append("extern uint8_t ", _varname, "[];"); source.append(); source.append("namespace"); diff --git a/contrib/enco/core/src/CppCode.h b/contrib/enco/core/src/CppCode.h index 6d18fc4..c52ea1d 100644 --- a/contrib/enco/core/src/CppCode.h +++ b/contrib/enco/core/src/CppCode.h @@ -27,7 +27,7 @@ namespace enco class CppCode { public: - CppCode(const Code *code) : _code{code} + CppCode(const std::string &varname, const Code *code) : _varname{varname}, _code{code} { // DO NOTHING } @@ -36,6 +36,7 @@ public: void dump(std::ostream &) const; private: + const std::string _varname; const Code *_code; }; diff --git a/contrib/enco/core/src/CppGen/Global.cpp b/contrib/enco/core/src/CppGen/Global.cpp index 11e81d3..cfc5e7f 100644 --- a/contrib/enco/core/src/CppGen/Global.cpp +++ b/contrib/enco/core/src/CppGen/Global.cpp @@ -20,6 +20,9 @@ #include +#include + +// TODO Remove unused code namespace { @@ -79,27 +82,12 @@ template <> GlobalOffset Global::constant(const std::vector &values) GlobalOffset Global::constant(const uint8_t *base, uint32_t size) { - uint32_t offset = _content.size(); + auto pos = _os.tellp(); + assert(pos != -1); - for (uint32_t n = 0; n < size; ++n) - { - _content.emplace_back(base[n]); - } - - return offset; -} + _os.write(reinterpret_cast(base), size); -void emit_as_initializer_list(std::ostream &os, const Global &global) -{ - if (global.size() > 0) - { - os << u08_to_hex(global.at(0)); - - for (uint32_t n = 1; n < global.size(); ++n) - { - os << ", " << u08_to_hex(global.at(n)); - } - } + return static_cast(pos); } } // namespace enco diff --git a/contrib/enco/core/src/CppGen/Global.h b/contrib/enco/core/src/CppGen/Global.h index 84ed508..c3b4fff 100644 --- a/contrib/enco/core/src/CppGen/Global.h +++ b/contrib/enco/core/src/CppGen/Global.h @@ -33,6 +33,12 @@ using GlobalOffset = uint32_t; class Global { public: + Global(std::ostream &os) : _os(os) + { + // DO NOTHING + } + +public: // @brief Create a global constant string (const char *) literal, and return variable name GlobalOffset constant(const std::string &value); @@ -42,22 +48,11 @@ public: // @brief Create a global constant array variable of byte (uint8_t) type GlobalOffset constant(const uint8_t *base, uint32_t size); -public: - uint32_t size(void) const { return _content.size(); } - uint8_t at(uint32_t n) const { return _content.at(n); } - private: - std::vector _content; + uint32_t _offset = 0; + std::ostream &_os; }; -void emit_as_initializer_list(std::ostream &os, const Global &global); - } // namespace enco -static inline std::ostream &operator<<(std::ostream &os, const enco::Global &global) -{ - emit_as_initializer_list(os, global); - return os; -} - #endif // __ENCO_GLOBAL_H__ diff --git a/contrib/enco/core/src/Transforms/GlobalDataGeneration.cpp b/contrib/enco/core/src/Transforms/GlobalDataGeneration.cpp index 76a5f7e..b244f8a 100644 --- a/contrib/enco/core/src/Transforms/GlobalDataGeneration.cpp +++ b/contrib/enco/core/src/Transforms/GlobalDataGeneration.cpp @@ -26,7 +26,6 @@ using nncc::foundation::make_unique; namespace { -std::map> global_ctx; std::map data_offset_ctx; std::map name_offset_ctx; std::map dims_offset_ctx; @@ -36,8 +35,6 @@ std::map dims_offset_ctx; namespace enco { -const Global *GlobalData::global(const coco::Module *m) { return global_ctx.at(m).get(); } - GlobalOffset GlobalData::data_offset(const ann::Operand *o) { return data_offset_ctx.at(o); } GlobalOffset GlobalData::name_offset(const coco::Input *in) { return name_offset_ctx.at(in); } @@ -46,10 +43,10 @@ GlobalOffset GlobalData::dims_offset(const coco::Input *in) { return dims_offset GlobalOffset GlobalData::name_offset(const coco::Output *out) { return name_offset_ctx.at(out); } GlobalOffset GlobalData::dims_offset(const coco::Output *out) { return dims_offset_ctx.at(out); } -void generate_global_data(enco::Code *code) +void generate_global_data(std::ostream &os, enco::Code *code) { auto m = code->module(); - auto global = make_unique(); + auto global = make_unique(os); for (uint32_t n = 0; n < code->ann()->count(); ++n) { @@ -84,8 +81,6 @@ void generate_global_data(enco::Code *code) name_offset_ctx[output] = global->constant(output->name()); dims_offset_ctx[output] = global->constant(dims); } - - global_ctx[m] = std::move(global); } } // namespace enco diff --git a/contrib/enco/core/src/Transforms/GlobalDataGeneration.h b/contrib/enco/core/src/Transforms/GlobalDataGeneration.h index 418f1dd..6298e15 100644 --- a/contrib/enco/core/src/Transforms/GlobalDataGeneration.h +++ b/contrib/enco/core/src/Transforms/GlobalDataGeneration.h @@ -25,8 +25,6 @@ namespace enco struct GlobalData { - static const Global *global(const coco::Module *); - static GlobalOffset data_offset(const ann::Operand *); static GlobalOffset name_offset(const coco::Input *); static GlobalOffset dims_offset(const coco::Input *); @@ -39,7 +37,7 @@ struct GlobalData * * NOTE Succeeding passes can access offsets via "GlobalData" */ -void generate_global_data(enco::Code *); +void generate_global_data(std::ostream &, enco::Code *); } // namespace enco diff --git a/contrib/enco/test/basic/000/CMakeLists.txt b/contrib/enco/test/basic/000/CMakeLists.txt index be1b68e..19863bd 100644 --- a/contrib/enco/test/basic/000/CMakeLists.txt +++ b/contrib/enco/test/basic/000/CMakeLists.txt @@ -3,6 +3,8 @@ ### set(PREFIX enco-basic-test-000) set(GENERATED_CPP ${PREFIX}.cpp) +set(GENERATED_ASM ${PREFIX}.embed.S) +set(GENERATED_BIN ${PREFIX}.bin) set(SOURCE_TARGET ${PREFIX}-src) set(LIB_TARGET ${PREFIX}-lib) @@ -17,6 +19,8 @@ add_custom_target(${SOURCE_TARGET} --backend-arg ${PREFIX} DEPENDS enco-cli ${PREFIX}-frontend) set_source_files_properties(${GENERATED_CPP} PROPERTIES GENERATED TRUE) -add_library(${LIB_TARGET} SHARED ${GENERATED_CPP}) +set_source_files_properties(${GENERATED_ASM} PROPERTIES GENERATED TRUE LANGUAGE C) +set_source_files_properties(${GENERATED_BIN} PROPERTIES GENERATED TRUE) +add_library(${LIB_TARGET} SHARED ${GENERATED_CPP} ${GENERATED_ASM}) target_link_libraries(${LIB_TARGET} PRIVATE ann_api) add_dependencies(${LIB_TARGET} ${SOURCE_TARGET}) diff --git a/contrib/enco/test/caffe/CMakeLists.txt b/contrib/enco/test/caffe/CMakeLists.txt index 78cee0a..6a282a5 100644 --- a/contrib/enco/test/caffe/CMakeLists.txt +++ b/contrib/enco/test/caffe/CMakeLists.txt @@ -7,6 +7,8 @@ function(get_test_configuration PREFIX) set(CAFFEMODEL_FILE "${PREFIX}.caffemodel" PARENT_SCOPE) set(CAFFEMODEL_TARGET enco_caffe_test_${PREFIX}_caffemodel PARENT_SCOPE) set(SOURCE_FILE ${PREFIX}.cpp PARENT_SCOPE) + set(ASM_FILE ${PREFIX}.embed.S PARENT_SCOPE) + set(BIN_FILE ${PREFIX}.bin PARENT_SCOPE) set(SOURCE_TARGET enco_caffe_test_${PREFIX}_generated PARENT_SCOPE) endfunction(get_test_configuration) @@ -54,6 +56,8 @@ foreach(MODEL IN ITEMS ${MODELS}) DEPENDS enco-cli enco_caffe_frontend ${CAFFEMODEL_TARGET} COMMENT "Generating ${SOURCE_FILE}") set_source_files_properties(${SOURCE_FILE} PROPERTIES GENERATED TRUE) + set_source_files_properties(${ASM_FILE} PROPERTIES GENERATED TRUE LANGUAGE C) + set_source_files_properties(${BIN_FILE} PROPERTIES GENERATED TRUE) list(APPEND CANDIDATES ${PREFIX}) endforeach(MODEL) @@ -73,7 +77,8 @@ foreach(PREFIX IN ITEMS ${CANDIDATES}) # Compile shared library (from generated C++ code) set(LIBRARY_TARGET enco_caffe_test_${PREFIX}_lib) - add_library(${LIBRARY_TARGET} SHARED ${SOURCE_FILE}) + add_library(${LIBRARY_TARGET} SHARED ${SOURCE_FILE} ${ASM_FILE}) + target_include_directories(${LIBRARY_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(${LIBRARY_TARGET} ann_api) add_dependencies(${LIBRARY_TARGET} ${SOURCE_TARGET}) endforeach(PREFIX) @@ -103,7 +108,8 @@ foreach(PREFIX IN ITEMS ${CANDIDATES}) set(BINDER_TARGET enco_caffe_test_${PREFIX}_binder) # Compile nnkit binder (from generated C++ code) - add_library(${BINDER_TARGET} SHARED binder.cpp ${SOURCE_FILE}) + add_library(${BINDER_TARGET} SHARED binder.cpp ${SOURCE_FILE} ${ASM_FILE}) + target_include_directories(${BINDER_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(${BINDER_TARGET} nnkit_intf_backend) target_link_libraries(${BINDER_TARGET} ann_api) target_link_libraries(${BINDER_TARGET} ann_ref_static)