blob: e258d892aabaadd224b80ba43336274dffa08005 [file]
#ifndef MXNET_OPERATOR_SUBGRAPH_TENSORRT_TENSORRT_INL_H_
#define MXNET_OPERATOR_SUBGRAPH_TENSORRT_TENSORRT_INL_H_
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* Copyright (c) 2019 by Contributors
* \file tensorrt-inl.h
* \brief TensorRT operation registration
* \author Marek Kolodziej, Clement Fuji Tsang
*/
#if MXNET_USE_TENSORRT
#include <onnx-tensorrt/NvOnnxParser.h>
#include <utility>
#include <string>
#include <vector>
#include "../common.h"
#include "../subgraph_property.h"
#include "nnvm_to_onnx-inl.h"
#include "./onnx_to_tensorrt.h"
namespace mxnet {
namespace op {
using int64 = ::google::protobuf::int64;
struct TRTParam {
std::unordered_map<std::string, uint32_t> inputs_to_idx;
std::unordered_map<std::string, uint32_t> outputs_to_idx;
std::unordered_map<std::string, NDArray> params_map;
};
struct TRTEngineParam {
TRTEngineParam(nvinfer1::ICudaEngine* trt_engine,
nvonnxparser::IParser* _parser,
const std::unordered_map<std::string, uint32_t> input_map,
const std::unordered_map<std::string, uint32_t> output_map) {
binding_order = std::make_shared<std::vector<std::pair<uint32_t, bool> > >();
bindings = std::make_shared<std::vector<void*> >();
binding_order->reserve(trt_engine->getNbBindings());
bindings->resize(trt_engine->getNbBindings());
for (int b = 0; b < trt_engine->getNbBindings(); ++b) {
const std::string& binding_name = trt_engine->getBindingName(b);
if (trt_engine->bindingIsInput(b)) {
binding_order->emplace_back(input_map.at(binding_name), true);
} else {
binding_order->emplace_back(output_map.at(binding_name), false);
}
}
trt_executor = trt_engine->createExecutionContext();
trt_parser = _parser;
}
~TRTEngineParam() {
trt_parser->destroy();
trt_executor->destroy();
}
nvinfer1::IExecutionContext* trt_executor;
nvonnxparser::IParser* trt_parser;
std::shared_ptr<std::vector<std::pair<uint32_t, bool> > > binding_order;
std::shared_ptr<std::vector<void*> > bindings;
};
class TensorrtSelector : public SubgraphSelector {
public:
const std::unordered_set<std::string> unconditionalTRTops = {
"BatchNorm",
"clip",
"Concat",
"Convolution",
"Dropout",
"elemwise_add",
"elemwise_sub",
"elemwise_mul",
"Flatten",
"FullyConnected",
"mean",
"Pad",
"relu",
"rsqrt",
"SoftmaxOutput"
};
const std::unordered_set<std::string> withWeightsOps = {
"BatchNorm",
"Convolution",
"FullyConnected"
};
bool isTRTCompatible(const nnvm::Node &n) {
const std::string op_name = n.op()->name;
if (op_name == "Pooling") {
return (n.attrs.dict.at("pool_type") == "avg" ||
n.attrs.dict.at("pool_type") == "max");
}
if (unconditionalTRTops.count(op_name)) {
return true;
}
if (op_name == "Activation") {
return n.attrs.dict.at("act_type") == "relu" ||
n.attrs.dict.at("act_type") == "tanh" ||
n.attrs.dict.at("act_type") == "sigmoid";
}
return false;
}
bool Select(const nnvm::Node &n) override {
return !n.is_variable() && isTRTCompatible(n);
}
bool SelectInput(const nnvm::Node &n, const nnvm::Node &new_node) override {
if (new_node.is_variable()) {
if (withWeightsOps.count(n.op()->name)) {
return n.inputs[0].node->attrs.name != new_node.attrs.name;
} else {
return false;
}
}
return isTRTCompatible(new_node);
}
bool SelectOutput(const nnvm::Node &n, const nnvm::Node &new_node) override {
return isTRTCompatible(new_node);
}
std::vector<nnvm::Node*> Filter(const std::vector<nnvm::Node*>& candidates) override {
bool found_one = false;
// TensorRT is interesting with at least 2 operations
for (auto& n : candidates) {
if (!n->is_variable()) {
if (found_one) {
return candidates;
} else {
found_one = true;
}
}
}
return std::vector<nnvm::Node*>();
}
};
class TensorrtProperty : public SubgraphProperty {
public:
static SubgraphPropertyPtr Create() {
return std::make_shared<TensorrtProperty>();
}
nnvm::NodePtr CreateSubgraphNode(const nnvm::Symbol &sym,
const int subgraph_id) const override {
nnvm::NodePtr n = nnvm::Node::Create();
nnvm::Symbol new_sym;
std::unique_copy(sym.outputs.begin(), sym.outputs.end(),
std::back_inserter(new_sym.outputs), [](
nnvm::NodeEntry lhs, nnvm::NodeEntry rhs) {
return lhs.index == rhs.index && lhs.node.get() == rhs.node.get();
});
n->attrs.name = "TensorRT" + std::to_string(subgraph_id);
n->attrs.op = Op::Get("_TensorRT");
CHECK(n->attrs.op);
n->attrs.subgraphs.emplace_back(std::make_shared<nnvm::Symbol>(new_sym));
std::ostringstream params_oss;
for (auto &e : new_sym.ListInputNames(nnvm::Symbol::kAll)) {
params_oss << e << ";";
}
auto tensorrt_params_names = params_oss.str();
tensorrt_params_names.pop_back();
n->attrs.dict["subgraph_params_names"] = tensorrt_params_names;
TRTParam param;
n->attrs.parsed = param;
n->op()->attr_parser(&(n->attrs));
return n;
}
SubgraphSelectorPtr CreateSubgraphSelector() const override {
return std::make_shared<TensorrtSelector>();
}
void ConnectSubgraphOutputs(const nnvm::NodePtr subgraph_node, \
std::vector<nnvm::NodeEntry*>* output_entries) const override {
std::vector<nnvm::NodeEntry>& outputs = subgraph_node->attrs.subgraphs[0]->outputs;
TRTParam& _params = nnvm::get<TRTParam>(subgraph_node->attrs.parsed);
for (size_t i = 0; i < outputs.size(); i++) {
auto& o = outputs[i];
for (auto& e : *output_entries) {
if (o.index == e->index && o.node.get() == e->node.get()) {
e->index = i;
e->node = subgraph_node;
// TODO(cfujitsang): For future support this would fail
// if the node have multiple outputs
_params.outputs_to_idx[o.node->attrs.name] = i;
}
}
}
subgraph_node->attrs.parsed = std::move(_params);
}
void ConnectSubgraphInputs(const nnvm::NodePtr subgraph_node,
std::vector<nnvm::NodeEntry*>* input_entries,
std::vector<nnvm::NodeEntry>* orig_input_entries) const override {
TRTParam& _params = nnvm::get<TRTParam>(subgraph_node->attrs.parsed);
subgraph_node->inputs.clear();
subgraph_node->inputs.resize(orig_input_entries->size());
for (size_t i = 0; i < orig_input_entries->size(); ++i) {
subgraph_node->inputs[i] = orig_input_entries->at(i);
_params.inputs_to_idx[input_entries->at(i)->node->attrs.name] = i;
}
subgraph_node->attrs.parsed = std::move(_params);
}
};
} // namespace op
} // namespace mxnet
#endif // MXNET_USE_TENSORRT
#endif // MXNET_OPERATOR_SUBGRAPH_TENSORRT_TENSORRT_INL_H_