[context/graph] Catch lifespan + 3-way execution order
authorParichay Kapoor <pk.kapoor@samsung.com>
Tue, 10 Aug 2021 10:21:27 +0000 (19:21 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Fri, 20 Aug 2021 08:15:58 +0000 (17:15 +0900)
This patch adds the setup to catch lifespan set by the layer developer
when requesting for tensors.
Further, the execution order now consists of 3 values - fowarding,
calcGradient and calcDerivative. The memory optimization will now be
done using the 3 functions to be called in order.

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
nntrainer/graph/graph_core.cpp
nntrainer/graph/graph_node.h
nntrainer/graph/network_graph.cpp
nntrainer/graph/network_graph.h
nntrainer/layers/layer_context.h
nntrainer/layers/layer_node.cpp
nntrainer/layers/layer_node.h
nntrainer/models/neuralnet.cpp
nntrainer/tensor/manager.cpp
nntrainer/tensor/manager.h
nntrainer/tensor/tensor_wrap_specs.h

index 687b202491323e99922ab03f0ef59edf3a9dbe5b..a3fb5fb3a78fa69e2edb91fc4a7e2cebf11f9e35 100644 (file)
@@ -89,8 +89,6 @@ void GraphCore::topologicalSort() {
 
   while (dfs_stack.empty() == false) {
     Sorted.push_back(dfs_stack.top());
-    Sorted.back()->setExecLoc(
-      {Sorted.size(), (node_list.size() * 2) - Sorted.size() + 1});
     dfs_stack.pop();
   }
 
index a2c260aea9f2ed9b9fd2b05191898de78ceea112..c2fcbab1b911d1d31a6aa947ec298c8051d8531e 100644 (file)
@@ -26,6 +26,20 @@ namespace nntrainer {
  */
 class GraphNode {
 public:
+  /**
+   * @brief Provides the time/order at which the node will be executed.
+   * @details This time will be finalized once the graph has been calculated.
+   * The three times given indicate the order with which the below three
+   * operations for each node are executed:
+   * 1. Forwarding
+   * 2. calcGradient
+   * 3. calcDerivative
+   * One constraint the three times is that they must be sorted in ascending
+   * order. This ensures that the operations are executed in the order of their
+   * listing.
+   */
+  typedef std::tuple<unsigned int, unsigned int, unsigned int> ExecutionOrder;
+
   /**
    * @brief     Destructor of Layer Class
    */
@@ -76,16 +90,16 @@ public:
    * @details   The two values represents the value for forward and backward
    * respectively
    */
-  virtual std::pair<unsigned int, unsigned int> getExecLoc() const = 0;
+  virtual ExecutionOrder getExecutionOrder() const = 0;
 
   /**
    * @brief     set the execution order/location of this node
    *
-   * @param     exec_loc the execution order/location of this node
+   * @param     exec_order the execution order/location of this node
    * @details   The two values represents the value for forward and backward
    * respectively
    */
-  virtual void setExecLoc(std::pair<unsigned int, unsigned int> exec_loc) = 0;
+  virtual void setExecutionOrder(ExecutionOrder exec_order_) = 0;
 };
 
 /**
index 4e7d982649154ba245745401b4dd45a8351a9350..42c04c3c257f16f1582958e5cb5d10c7990ae586 100644 (file)
@@ -60,6 +60,7 @@ int NetworkGraph::compile(const std::string &loss_type) {
   graph.topologicalSort();
 
   countNonTrainableLayersAtBegin();
+  setExecutionOrder();
 
   status = checkCompiledGraph();
   NN_RETURN_STATUS();
@@ -69,6 +70,21 @@ int NetworkGraph::compile(const std::string &loss_type) {
   return status;
 }
 
+void NetworkGraph::setExecutionOrder() {
+  auto node_count = graph.size();
+  /** @todo: remove backwarding count for non-trainble layers */
+  for (auto iter = cbegin(); iter != cend(); iter++) {
+    auto &node = *iter;
+    auto order_idx = iter - cbegin();
+    auto forward_order = order_idx;
+    auto calc_gradient_order = (node_count * 3) - (order_idx * 2) + 1;
+    /** calc derivative is called right after calc_gradient */
+    auto calc_derivative_order = calc_gradient_order + 1;
+    node->setExecutionOrder(
+      {forward_order, calc_gradient_order, calc_derivative_order});
+  }
+}
+
 void NetworkGraph::updateConnectionName(const std::string &from,
                                         const std::string &to) {
   for (auto iter = cbegin(); iter != cend(); iter++) {
index 033e7747f0fcb28b0add1b01ed76d6c964527480..49ad084f2ad538b50048c0816824bd232ea0a616 100644 (file)
@@ -464,6 +464,16 @@ private:
    * match and merging loss layers with activation layers if needed.
    */
   void finalizeLossLayer();
+
+  /**
+   * @brief Set the order of execution for all the nodes in the graph
+   *
+   * @details This sets the order of execution using the order from the
+   * topological sort. The order of forwarding matches the topological sort. The
+   * order for backwarding is in the exact reverse order. The calcDerivative()
+   * is expected to be called right after calcGradient().
+   */
+  void setExecutionOrder();
 };
 
 } // namespace nntrainer
index 74f48aa0e7c93f633b3862fc9793c409cd8bac23..49acac54384161a74df4b3a64b05042eebebb00b 100644 (file)
@@ -26,25 +26,6 @@ namespace nntrainer {
 class Weight;
 class Var_Grad;
 
-/**
- * @brief define the lifespan of the given tensor to reduce peak memory
- *
- */
-enum TensorLifespan {
-  FORWARD_FUNC_LIFESPAN,  /**< tensor must not be reset before during the
-                            forward function call, eg. temporary tensors
-                            needed during forward operations */
-  BACKWARD_FUNC_LIFESPAN, /**< tensor must not be reset before during the
-                            backward function call, eg. temporary tensors
-                            needed during backward operations */
-  ITERATION_LIFESPAN,     /**< tensor must not be reset until the owning layer
-                            finishes its execution in the current iteration,
-                            eg. hidden memory/cells of RNN */
-  EPOCH_LIFESPAN,         /**< tensor must not be reset before the epoch ends */
-  MAX_LIFESPAN, /**< tensor must not be reset until the end of the model
-                  execution, eg. layer weights */
-};
-
 /**
  * @class   Layer Context class for all layers
  * @brief   Class for Layer context
@@ -73,6 +54,11 @@ public:
     num_outputs(num_out),
     name(n) {}
 
+  /**
+   * @brief   get name by the layer
+   *
+   * @return name of the layer
+   */
   const std::string &getName() const { return name; }
 
   /**
@@ -192,7 +178,7 @@ public:
                 const Tensor::Initializer init = Tensor::Initializer::NONE,
                 bool trainable = false,
                 TensorLifespan lifespan = ITERATION_LIFESPAN) {
-    tensors_spec.emplace_back(dim, init, trainable, name);
+    tensors_spec.emplace_back(dim, init, trainable, name, lifespan);
     return tensors_spec.size() - 1;
   }
 
index 2d7e9d790566a5743539770bebf97f5e9bbaa005..d73140b2d89e1a7fa64dcd3c2081f3bdffeb4529 100644 (file)
@@ -124,7 +124,9 @@ LayerNode::LayerNode(std::unique_ptr<nntrainer::Layer> &&l) :
   activation_type(ActivationType::ACT_NONE),
   layer_node_props(new PropsType(props::Name(), props::Flatten(),
                                  props::Distribute(), props::Trainable(),
-                                 props::Loss())), regularization_loss(0.0f), exec_loc({0, 0}) {
+                                 props::Loss())),
+  regularization_loss(0.0f),
+  exec_order({0, 0, 0}) {
   if (layer && layer->getType() == TimeDistLayer::type) {
     std::get<props::Distribute>(*layer_node_props).set(true);
   }
index a6954b8752ad28c75dee872fbe44130fbd9e021f..bac8468c0ebbfc30e4781d21f2b7ad28f1c8082d 100644 (file)
@@ -140,15 +140,15 @@ public:
    *
    * @retval    the execution order/location of this node
    */
-  std::pair<unsigned int, unsigned int> getExecLoc() const { return exec_loc; }
+  ExecutionOrder getExecutionOrder() const { return exec_order; }
 
   /**
    * @brief     set the execution order/location of this node
    *
-   * @param     exec_loc the execution order/location of this node
+   * @param     exec_order the execution order/location of this node
    */
-  virtual void setExecLoc(std::pair<unsigned int, unsigned int> exec_loc_) {
-    exec_loc = exec_loc_;
+  void setExecutionOrder(ExecutionOrder exec_order_) {
+    exec_order = exec_order_;
   }
 
   /**
@@ -624,8 +624,8 @@ private:
    */
   std::unique_ptr<PropsType> layer_node_props; /**< properties for the node */
   float regularization_loss;
-  std::pair<int, int> exec_loc; /**< order/location of execution for this node
-                                   in forward and backward */
+  ExecutionOrder exec_order; /**< order/location of execution for this node
+                                   in forward and backwarding operations */
 
   /**
    * @brief setProperty by PropertyType
index 86d716f843eb7115e521b251d0986578a69a2262..6b0df0a6428237225f32ad97cc596aa873f93d96 100644 (file)
@@ -198,15 +198,7 @@ int NeuralNetwork::initialize() {
 /**
  * @brief     free layers
  */
-NeuralNetwork::~NeuralNetwork() {
-  model_graph.reset();
-
-  std::for_each(data_buffers.begin(), data_buffers.end(), [](auto &buffers) {
-    if (buffers) {
-      buffers->clear();
-    }
-  });
-}
+NeuralNetwork::~NeuralNetwork() { model_graph.reset(); }
 
 void NeuralNetwork::setLabels(sharedConstTensors label) {
   auto fill_label = [&label](auto const &layer_node) {
index 59ba59cf1bdb0af1613415b040a493efbda2fef3..82bab5abf813ca7ab820d6044cdc062599746b6d 100644 (file)
@@ -830,7 +830,8 @@ Manager::requestInputs(const GraphNode &node,
     [&count, &node](auto const &elem) {
       return std::make_tuple(elem, Tensor::Initializer::NONE, true,
                              node.getName() + std::string(":input") +
-                               std::to_string(count++));
+                               std::to_string(count++),
+                             ITERATION_LIFESPAN);
     });
   return requestTensors<Var_Grad>(node, inputs_spec, inputs_v2);
 }
@@ -848,7 +849,8 @@ Manager::requestOutputs(const GraphNode &node,
     [&count, &node](auto const &elem) {
       return std::make_tuple(elem, Tensor::Initializer::NONE, true,
                              node.getName() + std::string(":output") +
-                               std::to_string(count++));
+                               std::to_string(count++),
+                             ITERATION_LIFESPAN);
     });
   return requestTensors<Var_Grad>(node, outputs_spec, outputs_v2);
 }
index 10decf5e57dc6706fdc068e36d9dd6510c6381ef..02a1c02d9e673b2ac76f96fd8625ec6472d02f93 100644 (file)
@@ -440,10 +440,10 @@ private:
   std::vector<std::vector<std::unique_ptr<Var_Grad>>>
     tensors_v2; /**< extra tensors required by the layers */
 
-  std::unordered_map<std::string, std::pair<unsigned int, unsigned int>>
-    tensor_exec_loc; /**< stores the order/location at which a given tensor is
+  std::unordered_map<std::string, GraphNode::ExecutionOrder>
+    tensor_exec_order; /**< stores the order/location at which a given tensor is
                         going to be executed when the network is forwarded and
-                        abackwarded */
+                        backwarded */
 
   /**< Weights of all the layer in the model to be managed */
   std::vector<std::vector<std::reference_wrapper<Weight>>> weights;
@@ -592,12 +592,13 @@ private:
        * @todo maybe requesting tensor with same name should mean reusing the
        * tensor than giving the error
        */
-      if (tensor_exec_loc.find(ts_name) != tensor_exec_loc.end())
+      if (tensor_exec_order.find(ts_name) != tensor_exec_order.end())
         throw std::invalid_argument("Requesting tensor " + ts_name +
                                     " with same name");
       /**
-       * @todo set the exec_loc based on the set lifespan */
-      tensor_exec_loc[ts_name] = node.getExecLoc();
+       * @todo set the exec_order based on the set lifespan from the spec
+       */
+      tensor_exec_order[ts_name] = node.getExecutionOrder();
     }
 
     std::transform(tensors_list.begin(), tensors_list.end(),
index 88622b72a8ec63a4f6784d6a5477f90b7fe8ff8d..c6a1948917c328c90cbd3dc772f48bcdeea43bbf 100644 (file)
@@ -30,6 +30,25 @@ enum class WeightRegularizer {
   UNKNOWN /**< Unknown */
 };
 
+/**
+ * @brief define the lifespan of the given tensor to reduce peak memory
+ *
+ */
+enum TensorLifespan {
+  FORWARD_FUNC_LIFESPAN,  /**< tensor must not be reset before during the
+                            forward function call, eg. temporary tensors
+                            needed during forward operations */
+  BACKWARD_FUNC_LIFESPAN, /**< tensor must not be reset before during the
+                            backward function call, eg. temporary tensors
+                            needed during backward operations */
+  ITERATION_LIFESPAN,     /**< tensor must not be reset until the owning layer
+                            finishes its execution in the current iteration,
+                            eg. hidden memory/cells of RNN */
+  EPOCH_LIFESPAN,         /**< tensor must not be reset before the epoch ends */
+  MAX_LIFESPAN, /**< tensor must not be reset until the end of the model
+                  execution, eg. layer weights */
+};
+
 /**
  * @brief Specification of the Weight as a tensor wrapper
  *
@@ -44,9 +63,10 @@ typedef std::tuple<TensorDim, Tensor::Initializer, WeightRegularizer, float,
  * @brief Specification of the Var_Grad (trainable tensor) as a tensor wrapper
  *
  * @details The tuple values are dimension, initializer, need_gradient property,
- * and the name of the tensor object.
+ * the name, and lifespan of the Var_Grad object.
  */
-typedef std::tuple<TensorDim, Tensor::Initializer, bool, const std::string>
+typedef std::tuple<TensorDim, Tensor::Initializer, bool, const std::string,
+                   TensorLifespan>
   VarGradSpec;
 
 } // namespace nntrainer