diff options
Diffstat (limited to 'runtime/neurun/core/src/compiler/Linear.cc')
-rw-r--r-- | runtime/neurun/core/src/compiler/Linear.cc | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/runtime/neurun/core/src/compiler/Linear.cc b/runtime/neurun/core/src/compiler/Linear.cc new file mode 100644 index 000000000..b18dcea02 --- /dev/null +++ b/runtime/neurun/core/src/compiler/Linear.cc @@ -0,0 +1,317 @@ +/* + * 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 <algorithm> + +#include "Linear.h" + +#include "backend/IShapeFixer.h" +#include "backend/IConfig.h" +#include "backend/IConstantInitializer.h" +#include "backend/ITensorRegister.h" +#include "backend/Backend.h" +#include "compiler/SubTensorInfo.h" + +#include "util/logging.h" + +namespace neurun +{ +namespace compiler +{ + +Linear::Linear(ir::Graph &graph) : _graph(graph) +{ + // Get SubgraphSequence by topological sorting + { + ir::Subgraphs &subgraphs = _graph.subgraphs(); + ir::Operands &operands = _graph.operands(); + // subgraphs can't access a op_seq by an operand so that input_to_subgs can offer it + std::unordered_map<ir::OperandIndex, std::list<ir::SubgraphIndex>> input_to_subgs; + + // Get the relations between input/op_seq to be used for dfs-post-iter + // + // [0] # input -> _input_to_op_seqes[0] = {SUBG0} + // | + // [SUBG0] + // | + // [1]-----. # input -> _input_to_op_seqes[1] = {SUBG1, SUBG2} + // | | + // [SUBG1] [SUBG2] + // | | + // [2] [3] # input -> _input_to_op_seqes[2] = {SUBG3} + // \ / # input -> _input_to_op_seqes[3] = {SUBG3} + // [SUBG3] + // | + // [4] + subgraphs.iterate([&](const ir::SubgraphIndex &subg_idx, ir::OpSequence &subg) { + for (auto input : subg.getInputs()) + { + // only valid_inputs + const auto &operand = operands.at(input); + if (operand.isConstant()) + continue; + + auto it = input_to_subgs.find(input); + if (it == input_to_subgs.end()) + { + std::list<ir::SubgraphIndex> list{subg_idx}; + input_to_subgs[input] = list; + } + else + { + it->second.push_back(subg_idx); + } + } + }); + + std::unordered_map<ir::SubgraphIndex, bool> visited; + subgraphs.iterate( + [&](const ir::SubgraphIndex &index, const ir::OpSequence &) { visited[index] = false; }); + + std::function<void(const ir::SubgraphIndex &, ir::OpSequence &)> dfs_recursive = + [&](const ir::SubgraphIndex &index, ir::OpSequence &subg) -> void { + if (visited[index]) + return; + visited[index] = true; + + // The outputs should be not constants + for (auto output : subg.getOutputs()) + { + const auto it = input_to_subgs.find(output); + if (it != input_to_subgs.end()) + { + const auto &subg_index_list = it->second; + for (const auto &index : subg_index_list) + { + auto &subg = subgraphs.at(index); + dfs_recursive(index, subg); + } + } + } + + _elements.emplace_back(&subgraphs.at(index), _graph.getLowerInfo(index)); + }; + + subgraphs.iterate(dfs_recursive); + + // All of the nodes must have been visited. + assert(std::all_of(visited.begin(), visited.end(), + [](const std::pair<const ir::SubgraphIndex, bool> &v) { return v.second; })); + + // NOTE. Now these op_seq are on the reverse order + std::reverse(_elements.begin(), _elements.end()); + } + + { + const auto &backendToString = [](const neurun::backend::Backend *backend) { + assert(backend); + std::string str; + str += backend->config()->id(); + str += " "; + return "{ " + str + "}"; + }; + + VERBOSE(Linear) << "Final SubgraphSequence" << std::endl; + for (const auto &element : _elements) + { + const auto subg = element.op_seq; + const auto lower_info = element.lower_info; + VERBOSE(Linear) << "* SUBG" + << " " << backendToString(lower_info->backend()) << " " << subg->getStr() + << std::endl; + } + } +} + +void Linear::accept(ir::OperationVisitor &&visitor) const +{ + for (const auto &e : _elements) + { + e.op_seq->accept(visitor); + } +} + +void Linear::planTensors() +{ + ir::OperandIndexMap<std::shared_ptr<backend::ITensorBuilder>> tensor_builder_map; + + // NOTE + // While current ITensorBuilder exposes registerSubTensorInfo for subtensor, + // this stage uses registerSubTensorInfo() and notify{First|Last}Use() + // but handling subtensor should be processed on each backend. See #5726. + ir::OperandIndexMap<uint32_t> uses_map; + ir::OperandIndexMap<uint32_t> def_map; + ir::OperandIndexSequence constants; + + iterate([&](const neurun::compiler::Linear::Element &element) { + const auto backend = element.lower_info->backend(); + const auto tensor_register = + _graph.backend_resolver()->getBackendContext(backend)->tensor_register; + tensor_register->registerTensors(*element.op_seq, _graph.getLowerInfo()); + }); + + // Prepare scanning + _graph.operands().iterate([&](const ir::OperandIndex &ind, const ir::Operand &obj) { + const auto lower_info = _graph.getLowerInfo(ind); + // TODO Remove if neurun doesn't support anymore such as + // GeneratedTests.reshape_quant8_weights_as_inputs + if (lower_info->def_factors().size() == 0 && lower_info->use_factors().size() == 0 && + !_graph.getInputs().contains(ind)) + { + VERBOSE(LINEAR) << "Operand #" << ind.value() << " will be not used. no more process." + << std::endl; + return; + } + + uses_map[ind] = obj.getUses().size(); + def_map[ind] = obj.getDef().size(); // should be 1 or 0 + + bool is_const = obj.isConstant(); + if (is_const) + { + constants.append(ind); + } + + for (auto factor : lower_info->def_factors()) + { + auto backend = factor.backend(); + auto tensor_builder = _graph.backend_resolver()->getBackendContext(backend)->tensor_builder; + + if (!tensor_builder->isRegistered(ind)) + { + // These tensors do not exist in any op_seq (No use and def) + // These tensors cannot be a SubTensor + assert(obj.parent_info() == nullptr); + + const auto info = obj.info(); + const auto backend_layout = lower_info->def_factors().getOnlyElement().layout(); + // TODO Change tensor info to have permuted shape + tensor_builder->registerTensorInfo(ind, info, backend_layout, is_const); + } + + tensor_builder_map[ind] = 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]++; + } + + // Start scanning to do notify{First|Last}Use for each tensor + + // If a tensor is a constant, increase the use of the tensor. + // It makes the tensor not be dealloced. It means these will be deallocated last. + // And allocate constant operands first + VERBOSE(LINEAR) << "TENSORS as CONSTANT" << std::endl; + for (const auto &ind : constants) + { + uses_map[ind]++; + tensor_builder_map[ind]->notifyFirstUse(ind); + } + + // Allocate Model's inputs + VERBOSE(LINEAR) << "TENSORS as MODEL INPUT" << std::endl; + for (const auto &ind : _graph.getInputs()) + { + auto tensor_builder = tensor_builder_map[ind]; + if (!tensor_builder) // for GeneratedTests.xxx_weights_as_inputs + continue; + tensor_builder->notifyFirstUse(ind); + } + + // At each operation, + // 1. Scan DEF of outputs. If the DEF, allocate it + // 2. Scan USE of inputs. Decrease the USE and deallocate if the USE is 0 + VERBOSE(LINEAR) << "TENSORS" << std::endl; + for (const auto &e : _elements) + { + for (const auto &op : e.op_seq->operations()) + { + for (const auto &ind : op.node->getOutputs()) + { + assert(def_map.find(ind) != def_map.end()); + if (def_map[ind]) + { + def_map[ind] = 0; + tensor_builder_map[ind]->notifyFirstUse(ind); + } + } + + for (const auto &ind : op.node->getInputs()) + { + assert(uses_map.find(ind) != uses_map.end()); + assert(uses_map[ind] > 0); + uses_map[ind]--; + if (uses_map[ind] == 0) + { + tensor_builder_map[ind]->notifyLastUse(ind); + } + } + } + } + + // Dispose and validate + for (const auto &ind : _graph.getOutputs()) + { + --uses_map[ind]; + if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice + { + tensor_builder_map[ind]->notifyLastUse(ind); + } + } + + for (const auto &ind : constants) + { + --uses_map[ind]; + if (uses_map[ind] == 0) // To prevent notifyLastUse from being called twice + { + tensor_builder_map[ind]->notifyLastUse(ind); + } + } + + assert( + std::all_of(uses_map.begin(), uses_map.end(), + [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; })); + + assert( + std::all_of(def_map.begin(), def_map.end(), + [](std::pair<const ir::OperandIndex, uint32_t> it) { return it.second == 0; })); +} + +void Linear::iterate(const std::function<void(const Element &element)> &fn) const +{ + for (const auto &e : _elements) + { + fn(e); + } +} + +void Linear::generateConstantInitializers(void) const +{ + iterate([&](const compiler::Linear::Element &element) { + auto backend = element.lower_info->backend(); + + auto constant_initializer = + _graph.backend_resolver()->getBackendContext(backend)->constant_initializer; + constant_initializer->generate(*element.op_seq, _graph.operands()); + }); +} + +} // namespace compiler +} // namespace neurun |