[enco] Emit global weight data as a binary file (#1741)
author박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Thu, 4 Oct 2018 07:00:24 +0000 (16:00 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Thu, 4 Oct 2018 07:00:24 +0000 (16:00 +0900)
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 <jh1302.park@samsung.com>
contrib/enco/core/src/AsmCode.cpp [new file with mode: 0644]
contrib/enco/core/src/AsmCode.h [new file with mode: 0644]
contrib/enco/core/src/Backend.cpp
contrib/enco/core/src/CppCode.cpp
contrib/enco/core/src/CppCode.h
contrib/enco/core/src/CppGen/Global.cpp
contrib/enco/core/src/CppGen/Global.h
contrib/enco/core/src/Transforms/GlobalDataGeneration.cpp
contrib/enco/core/src/Transforms/GlobalDataGeneration.h
contrib/enco/test/basic/000/CMakeLists.txt
contrib/enco/test/caffe/CMakeLists.txt

diff --git a/contrib/enco/core/src/AsmCode.cpp b/contrib/enco/core/src/AsmCode.cpp
new file mode 100644 (file)
index 0000000..221cb34
--- /dev/null
@@ -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 (file)
index 0000000..c438928
--- /dev/null
@@ -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 <ostream>
+#include <string>
+
+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__
index 4270d3d..d436e98 100644 (file)
@@ -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
index 512ce80..5ca0195 100644 (file)
@@ -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<const ANNBinder *, SubnetInfo> 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");
index 6d18fc4..c52ea1d 100644 (file)
@@ -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;
 };
 
index 11e81d3..cfc5e7f 100644 (file)
@@ -20,6 +20,9 @@
 
 #include <pp/Format.h>
 
+#include <cassert>
+
+// TODO Remove unused code
 namespace
 {
 
@@ -79,27 +82,12 @@ template <> GlobalOffset Global::constant(const std::vector<uint32_t> &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<const char *>(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<GlobalOffset>(pos);
 }
 
 } // namespace enco
index 84ed508..c3b4fff 100644 (file)
@@ -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<uint8_t> _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__
index 76a5f7e..b244f8a 100644 (file)
@@ -26,7 +26,6 @@ using nncc::foundation::make_unique;
 namespace
 {
 
-std::map<const coco::Module *, std::unique_ptr<enco::Global>> global_ctx;
 std::map<const ann::Operand *, enco::GlobalOffset> data_offset_ctx;
 std::map<const coco::Arg *, enco::GlobalOffset> name_offset_ctx;
 std::map<const coco::Arg *, enco::GlobalOffset> dims_offset_ctx;
@@ -36,8 +35,6 @@ std::map<const coco::Arg *, enco::GlobalOffset> 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<Global>();
+  auto global = make_unique<Global>(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<uint32_t>(dims);
   }
-
-  global_ctx[m] = std::move(global);
 }
 
 } // namespace enco
index 418f1dd..6298e15 100644 (file)
@@ -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
 
index be1b68e..19863bd 100644 (file)
@@ -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})
index 78cee0a..6a282a5 100644 (file)
@@ -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)