// DO NOTHING
}
-void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind,
- const ::arm_compute::TensorInfo &info)
+void TensorBuilder::notifyFirstUse(const graph::operand::Index &ind,
+ const ::arm_compute::TensorInfo &info)
{
assert(_tensors.size() == 0);
_tensor_info_map.insert({ind, info});
}
+void TensorBuilder::notifyLastUse(const graph::operand::Index &)
+{
+ // DO NOTHING
+}
+
void TensorBuilder::prepare(void)
{
assert(_tensors.size() == 0);
public:
TensorBuilder();
- virtual void mark(const ::neurun::graph::operand::Index &ind,
- const ::arm_compute::TensorInfo &info) override;
+ virtual void notifyFirstUse(const graph::operand::Index &,
+ const ::arm_compute::TensorInfo &) override;
+ virtual void notifyLastUse(const graph::operand::Index &) override;
+
virtual void prepare(void) override;
virtual void allocate(void) override;
{
assert(!_base && _pos != 0);
- VERBOSE(BP_ALLOC) << "final position: " << _pos << std::endl;
-
_base = new uint8_t[_pos];
+
+ VERBOSE(BP_ALLOC) << "final position: " << _pos << std::endl;
+ VERBOSE(BP_ALLOC) << "base pointer: " << static_cast<void *>(_base) << std::endl;
}
void BumpAllocator::free(const graph::operand::Index &index)
#include "operand/Object.h"
#include "MemoryAllocator.h"
+#include "logging.h"
namespace neurun
{
// DO NOTHING
}
-void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind,
- const ::arm_compute::TensorInfo &info)
+void TensorBuilder::notifyFirstUse(const graph::operand::Index &ind,
+ const ::arm_compute::TensorInfo &info)
{
- assert(_tensors.size() == 0);
+ assert(_mem_alloc);
_tensor_info_map.insert({ind, info});
+
+ const auto size = info.total_size();
+ auto mem_blk = _mem_alloc->allocate(ind, size);
+ _tensor_mem_map[ind] = mem_blk;
+
+ VERBOSE(CPU_TENSORBUILDER) << "ASSIGN(#" << ind.value() << "): mem_blk[" << mem_blk.offset << ", "
+ << mem_blk.size << "]" << std::endl;
+}
+
+void TensorBuilder::notifyLastUse(const graph::operand::Index &ind)
+{
+ assert(_mem_alloc);
+
+ _mem_alloc->free(ind);
+
+ VERBOSE(CPU_TENSORBUILDER) << "UNASSIGN(#" << ind.value() << ")" << std::endl;
}
void TensorBuilder::prepare(void)
assert(_tensors.size() == 0);
assert(_mem_alloc);
- for (auto &entry : _tensor_info_map)
- {
- auto ind = entry.first;
- const auto &info = entry.second;
- auto tensor = std::make_shared<operand::Tensor>(info);
- _tensors[ind] = tensor;
- // If we do not make tensor here currently, stages would cause segment fault
-
- const auto size = info.total_size(); // NOTE This size may not be accurate
- auto mem_blk = _mem_alloc->allocate(ind, size);
- _tensor_mem_map[ind] = mem_blk;
- }
- assert(_tensor_info_map.size() == _tensor_mem_map.size());
-
- // TODO below code can be moved in TensorBuild::allocate()
- // if StageGerator was modified like
- // from
- // fn->configure(ifm_alloc->buffer(), param.ifm_shape, ker_alloc->buffer(), param.ker_shape,
- // to
- // fn->configure(ifm_alloc, param.ifm_shape, ker_alloc, param.ker_shape,
_mem_alloc->finalize();
assert(_mem_alloc->base());
{
auto ind = entry.first;
auto mem_blk = entry.second;
- auto &tensor = _tensors[ind];
- tensor->setBuffer(_mem_alloc->base() + mem_blk.offset);
+ const auto &info = _tensor_info_map[ind];
+
+ uint8_t *buffer = _mem_alloc->base() + mem_blk.offset;
+ auto tensor = std::make_shared<operand::Tensor>(info);
+ tensor->setBuffer(buffer);
+ _tensors[ind] = tensor;
+
+ VERBOSE(CPU_TENSORBUILDER) << "TENSOR(#" << ind.value() << "): " << static_cast<void *>(buffer)
+ << std::endl;
+
+ // If we do not make tensor here currently, stages would cause segment fault
}
}
void TensorBuilder::allocate(void)
{
- assert(_tensor_info_map.size() == _tensors.size());
-
// NOTE For now nothing to do. Allocation is done in prepare stage, which is wrong
}
public:
TensorBuilder();
- virtual void mark(const ::neurun::graph::operand::Index &ind,
- const ::arm_compute::TensorInfo &info) override;
+ virtual void notifyFirstUse(const graph::operand::Index &,
+ const ::arm_compute::TensorInfo &) override;
+ virtual void notifyLastUse(const graph::operand::Index &) override;
+
virtual void prepare(void) override;
virtual void allocate(void) override;
using IterateFunction = std::function<void(const graph::operand::Index &)>;
virtual ~ITensorBuilder(void) = default;
- virtual void mark(const ::neurun::graph::operand::Index &ind,
- const ::arm_compute::TensorInfo &info) = 0;
+
+ virtual void notifyFirstUse(const graph::operand::Index &, const ::arm_compute::TensorInfo &) = 0;
+ virtual void notifyLastUse(const graph::operand::Index &) = 0;
+
// TODO Add an interface for adding subsumption info
virtual void prepare(void) = 0;
virtual void allocate(void) = 0;
neurun::codegen::PlanBuilder plan_builder{plan};
- auto tensor_builders = linear->markTensors();
-
linear->accept(neurun::codegen::Planner{operands, plan_builder});
+ auto tensor_builders = linear->planTensors();
+
// TODO Add optimization passes
plan_builder.finalize(tensor_builders);
#include "backend/interface/IStageGenerator.h"
#include "internal/Convert.h"
+#include "logging.h"
+
namespace neurun
{
namespace linear
}
}
-backend::TensorBuilderSet Linear::markTensors() const
+backend::TensorBuilderSet Linear::planTensors()
{
+ using ITensorBuilderPtr = std::shared_ptr<backend::ITensorBuilder>;
+ using FnOnTensorBuilder =
+ std::function<void(const graph::operand::Index &ind, ITensorBuilderPtr)>;
+
+ const auto &operands = _graph.operands();
+ auto iterTensorBuilders = [&operands](const graph::operand::Index &ind, FnOnTensorBuilder fn) {
+ const auto &obj = operands.at(ind);
+ for (auto backend : obj.lower_info()->def_backends())
+ {
+ auto tensor_builder = backend->tensor_builder();
+ fn(ind, tensor_builder);
+ }
+ };
+
backend::TensorBuilderSet tensor_builders;
+ std::unordered_map<graph::operand::Index, uint32_t> uses_map;
+ std::vector<graph::operand::Index> constants;
+
_graph.operands().iterate(
[&](const graph::operand::Index &ind, const graph::operand::Object &obj) {
- for (auto backend : obj.lower_info()->def_backends())
- {
- auto tensor_builder = backend->tensor_builder();
- const auto info = ::internal::asTensorInfo(obj.shape(), obj.typeInfo());
+ uses_map[ind] = obj.getUses().size();
- tensor_builder->mark(ind, info);
+ // If a tensor is a constant, increase the use of the tensor.
+ // It makes the tensor not be dealloced.
+ if (obj.getUsage() == graph::operand::OperandUsage::CONSTANT)
+ {
+ constants.push_back(ind);
+ uses_map[ind]++;
+ }
+ // Prepare tensor builders to be returned
+ iterTensorBuilders(ind, [&tensor_builders](const graph::operand::Index &,
+ ITensorBuilderPtr tensor_builder) {
tensor_builders.insert(tensor_builder);
- }
+ });
});
+ // If a tensor is model output, increase the use of the tensor.
+ // This aim is same to above one.
+ for (const auto &ind : _graph.getOutputs())
+ {
+ uses_map[ind]++;
+ }
+
+ // Allocate constant operands first
+ VERBOSE(LINEAR) << "TENSORS as CONSTANT" << std::endl;
+ for (const auto &ind : constants)
+ {
+ const auto &obj = operands.at(ind);
+ const auto info = ::internal::asTensorInfo(obj.shape(), obj.typeInfo());
+ iterTensorBuilders(ind,
+ [&info](const graph::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyFirstUse(ind, info);
+ });
+ }
+
+ // Allocate Model's inputs
+ VERBOSE(LINEAR) << "TENSORS as MODEL INPUT" << std::endl;
+ for (const auto &ind : _graph.getInputs())
+ {
+ const auto &obj = operands.at(ind);
+ const auto info = ::internal::asTensorInfo(obj.shape(), obj.typeInfo());
+ iterTensorBuilders(ind,
+ [&info](const graph::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyFirstUse(ind, info);
+ });
+ }
+
+ // At each operation,
+ // 1. Scan USE of inputs. Decrease the USE and deallocate if the USE is 0
+ // 2. Scan DEF of outputs. If the DEF, allocate it
+ VERBOSE(LINEAR) << "TENSORS" << std::endl;
+ for (const auto op : _operations)
+ {
+ for (const auto &ind : op->getOutputs())
+ {
+ const auto &obj = operands.at(ind);
+ if (obj.getDef().size())
+ {
+ const auto info = ::internal::asTensorInfo(obj.shape(), obj.typeInfo());
+ iterTensorBuilders(
+ ind, [&info](const graph::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyFirstUse(ind, info);
+ });
+ }
+ }
+
+ for (const auto &ind : op->getInputs())
+ {
+ uses_map[ind]--;
+ if (uses_map[ind] == 0)
+ {
+ iterTensorBuilders(ind,
+ [](const graph::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyLastUse(ind);
+ });
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ // Now, model outputs should be not deallocated
+ for (const auto &ind : _graph.getOutputs())
+ assert(uses_map[ind] > 0);
+#endif
+
return tensor_builders;
}
void accept(graph::operation::NodeVisitor &&visitor) const;
// TODO Should not return TensorBuilderSet
- backend::TensorBuilderSet markTensors() const;
+ backend::TensorBuilderSet planTensors();
private:
const graph::Graph &_graph;