constant fold approve multi output

This commit is contained in:
zhengjun10 2020-09-01 17:44:59 +08:00
parent f42b3bbfbc
commit e121bcd3be
4 changed files with 289 additions and 57 deletions

View File

@ -236,6 +236,136 @@ MetaGraphTptr BuildMixGraph() {
// final output
return meta_graph;
}
MetaGraphTptr BuildSplitGraph() {
auto meta_graph = std::make_shared<schema::MetaGraphT>();
meta_graph->name = "graph";
// slice node
auto split_node = std::make_unique<schema::CNodeT>();
split_node->inputIndex = {0};
split_node->outputIndex = {1, 2};
split_node->primitive = std::make_unique<schema::PrimitiveT>();
split_node->primitive->value.type = schema::PrimitiveType_Split;
std::unique_ptr<schema::SplitT> attr = std::make_unique<schema::SplitT>();
attr->numberSplit = 2;
attr->splitDim = 1;
split_node->primitive->value.value = attr.release();
split_node->name = "split";
meta_graph->nodes.emplace_back(std::move(split_node));
meta_graph->inputIndex = {0, 3, 4};
meta_graph->outputIndex = {5, 6};
auto mul_node1 = std::make_unique<schema::CNodeT>();
mul_node1->inputIndex = {1, 3};
mul_node1->outputIndex = {5};
mul_node1->primitive = std::make_unique<schema::PrimitiveT>();
mul_node1->primitive->value.type = schema::PrimitiveType_Mul;
std::unique_ptr<schema::MulT> mul_attr = std::make_unique<schema::MulT>();
mul_node1->primitive->value.value = mul_attr.release();
mul_node1->name = "mul1";
meta_graph->nodes.emplace_back(std::move(mul_node1));
auto mul_node2 = std::make_unique<schema::CNodeT>();
mul_node2->inputIndex = {2, 4};
mul_node2->outputIndex = {6};
mul_node2->primitive = std::make_unique<schema::PrimitiveT>();
mul_node2->primitive->value.type = schema::PrimitiveType_Mul;
std::unique_ptr<schema::MulT> mul2_attr = std::make_unique<schema::MulT>();
mul_node2->primitive->value.value = mul2_attr.release();
mul_node2->name = "mul2";
meta_graph->nodes.emplace_back(std::move(mul_node2));
// input 0: data1
auto input0 = std::make_unique<schema::TensorT>();
input0->nodeType = schema::NodeType::NodeType_ValueNode;
input0->format = schema::Format_NHWC;
input0->dataType = TypeId::kNumberTypeFloat32;
input0->dims = {1, 2, 2, 3};
input0->offset = -1;
auto input0_data = new(std::nothrow) float[2 * 2 * 3];
for (auto i = 0; i < 2 * 2 * 3; i++) {
input0_data[i] = i;
}
input0->data.resize(sizeof(float) * 2 * 2 * 3);
memcpy(input0->data.data(), input0_data, 2 * 2 * 3 * sizeof(float));
delete[] input0_data;
meta_graph->allTensors.emplace_back(std::move(input0));
// split output1
auto split_output1 = std::make_unique<schema::TensorT>();
split_output1->nodeType = schema::NodeType::NodeType_Parameter;
split_output1->format = schema::Format_NHWC;
split_output1->dataType = TypeId::kNumberTypeFloat32;
split_output1->dims = {1, 1, 2, 3};
split_output1->offset = -1;
split_output1->data.resize(sizeof(float) * 1 * 2 * 3);
auto split_output_data1 = new(std::nothrow) float[1 * 2 * 3];
memcpy(split_output1->data.data(), split_output_data1, 1 * 2 * 3 * sizeof(float));
delete[] split_output_data1;
meta_graph->allTensors.emplace_back(std::move(split_output1));
// split output2
auto split_output2 = std::make_unique<schema::TensorT>();
split_output2->nodeType = schema::NodeType::NodeType_Parameter;
split_output2->format = schema::Format_NHWC;
split_output2->dataType = TypeId::kNumberTypeFloat32;
split_output2->dims = {1, 1, 2, 3};
split_output2->offset = -1;
split_output2->data.resize(sizeof(float) * 1 * 2 * 3);
auto split_output_data2 = new(std::nothrow) float[1 * 2 * 3];
memcpy(split_output2->data.data(), split_output_data2, 1 * 2 * 3 * sizeof(float));
delete[] split_output_data2;
meta_graph->allTensors.emplace_back(std::move(split_output2));
// input 1: data2
auto input1 = std::make_unique<schema::TensorT>();
input1->nodeType = schema::NodeType::NodeType_ValueNode;
input1->format = schema::Format_NHWC;
input1->dataType = TypeId::kNumberTypeFloat32;
input1->dims = {1, 1, 2, 3};
input1->offset = -1;
input1->data.resize(sizeof(float) * 2 * 3);
auto input1_data = new(std::nothrow) float[2 * 3];
for (auto i = 0; i < 2 * 3; i++) {
input1_data[i] = i;
}
memcpy(input1->data.data(), input1_data, 2 * 3 * sizeof(float));
delete[] input1_data;
meta_graph->allTensors.emplace_back(std::move(input1));
// input 2: data3
auto input2 = std::make_unique<schema::TensorT>();
input2->nodeType = schema::NodeType::NodeType_ValueNode;
input2->format = schema::Format_NHWC;
input2->dataType = TypeId::kNumberTypeFloat32;
input2->dims = {1, 1, 2, 3};
input2->offset = -1;
input2->data.resize(sizeof(float) * 2 * 3);
auto input2_data = new(std::nothrow) float[2 * 3];
for (auto i = 0; i < 2 * 3; i++) {
input2_data[i] = 10;
}
memcpy(input2->data.data(), input2_data, 2 * 3 * sizeof(float));
delete[] input2_data;
meta_graph->allTensors.emplace_back(std::move(input2));
// final mul output1
auto mul_output = std::make_unique<schema::TensorT>();
mul_output->nodeType = schema::NodeType::NodeType_Parameter;
mul_output->format = schema::Format_NHWC;
mul_output->dataType = TypeId::kNumberTypeFloat32;
mul_output->dims = {1, 1, 2, 3};
meta_graph->allTensors.emplace_back(std::move(mul_output));
// final mul output2
auto mul_output2 = std::make_unique<schema::TensorT>();
mul_output2->nodeType = schema::NodeType::NodeType_Parameter;
mul_output2->format = schema::Format_NHWC;
mul_output2->dataType = TypeId::kNumberTypeFloat32;
mul_output2->dims = {1, 1, 2, 3};
meta_graph->allTensors.emplace_back(std::move(mul_output2));
return meta_graph;
}
} // namespace
TEST_F(ConstantFoldingFusionTest, TestADDConstantFold) {
auto meta_graph = BuildGraph(schema::PrimitiveType_Add, new schema::AddT);
@ -483,4 +613,19 @@ TEST_F(ConstantFoldingFusionTest, TestCastDimsConstantFold) {
auto new_meta_graph = lite::Export(new_graph);
ASSERT_EQ(new_meta_graph->nodes.size(), 0);
}
TEST_F(ConstantFoldingFusionTest, TestSplitConstantFold) {
auto meta_graph = BuildSplitGraph();
auto input_tensor = meta_graph->allTensors.at(0).get();
input_tensor->dataType = kNumberTypeFloat32;
auto func_graph = lite::ModelParser::Fb2Anf(meta_graph.get());
auto optimizer = std::make_shared<opt::GraphOptimizer>();
auto pm = std::make_shared<opt::PassManager>("test", false);
pm->AddPass(std::make_shared<opt::ConstFoldPass>());
optimizer->AddPassManager(pm);
FuncGraphPtr new_graph = optimizer->Optimize(func_graph);
ASSERT_NE(nullptr, new_graph);
auto new_meta_graph = lite::Export(new_graph);
ASSERT_EQ(new_meta_graph->nodes.size(), 0);
}
} // namespace mindspore

View File

@ -319,7 +319,7 @@ schema::PrimitiveType GetCNodeType(const BaseRef &n) {
if (utils::isa<PrimitiveCPtr>(value)) {
auto primitive = value->cast<PrimitiveCPtr>();
MS_ASSERT(primitive != nullptr);
return (schema::PrimitiveType)primitive->Type();
return (schema::PrimitiveType) primitive->Type();
} else if (utils::isa<Primitive>(value)) {
auto primitive = value->cast<PrimitivePtr>();
MS_ASSERT(primitive != nullptr);
@ -392,8 +392,8 @@ size_t GetOutputTensorNum(const AnfNodePtr &node) {
bool IsMultiOutputTensors(const FuncGraphPtr &graph, const AnfNodePtr &node) {
auto output_node_list = GetRealNodeUsedList(graph, node);
if (output_node_list->size() != 1) {
MS_LOG(DEBUG) << "fusion node has multi output nodes";
return true;
MS_LOG(DEBUG) << "fusion node has multi output nodes";
return true;
}
return false;
}
@ -412,5 +412,50 @@ std::shared_ptr<std::vector<std::pair<AnfNodePtr, int>>> GetRealNodeUsedList(con
std::copy(output_info_list.begin(), output_info_list.end(), std::back_inserter(*output_node_list));
return output_node_list;
}
size_t GetTupleGetItemOutIndex(const CNodePtr &tuple_get_item) {
MS_ASSERT(tuple_get_item != nullptr);
if (tuple_get_item->size() != kTupleGetItemInputSize) {
MS_LOG(ERROR) << "The node tuple_get_item must have 2 inputs!";
return -1;
}
auto output_index_value_node = tuple_get_item->input(kInputNodeOutputIndexInTupleGetItem);
MS_ASSERT(output_index_value_node != nullptr);
auto value_node = output_index_value_node->cast<ValueNodePtr>();
MS_ASSERT(value_node != nullptr);
return IntToSize(GetValue<int>(value_node->value()));
}
std::shared_ptr<std::vector<std::pair<AnfNodePtr, int>>> GetRealNodeUsedListByOutputIdx(const FuncGraphPtr &graph,
const AnfNodePtr &node,
size_t output_index) {
MS_ASSERT(graph != nullptr);
MS_ASSERT(node != nullptr);
auto output_node_list = std::make_shared<std::vector<std::pair<AnfNodePtr, int>>>();
auto manager = graph->manager();
MS_ASSERT(manager != nullptr);
auto iter = manager->node_users().find(node);
if (iter == manager->node_users().end()) {
MS_LOG(ERROR) << "node has no output in manager";
return output_node_list;
}
auto output_info_list = iter->second;
for (const auto &output_info : output_info_list) {
size_t used_output_index;
if (GetCNodeType(output_info.first) == schema::PrimitiveType_TupleGetItem) {
used_output_index = GetTupleGetItemOutIndex(utils::cast<CNodePtr>(output_info.first));
} else if (GetCNodeType(node) == schema::PrimitiveType_TupleGetItem) {
used_output_index = output_index;
} else {
if (output_index != 0) {
MS_LOG(ERROR) << "node has no output in manager";
return output_node_list;
}
return output_node_list;
}
if (used_output_index == output_index) {
output_node_list->push_back(output_info);
}
}
return output_node_list;
}
} // namespace opt
} // namespace mindspore

View File

@ -63,6 +63,8 @@ bool CheckIsAllInputsParam(const AnfNodePtr &node);
size_t GetOutputTensorNum(const AnfNodePtr &node);
bool IsMultiOutputTensors(const FuncGraphPtr &graph, const AnfNodePtr &node);
size_t GetTupleGetItemOutIndex(const CNodePtr &tuple_get_item);
} // namespace opt
} // namespace mindspore
#endif // MINDSPORE_LITE_SRC_PASS_COMMON_GLLO_UTILS_H_

View File

@ -41,7 +41,7 @@ std::vector<Tensor *> GetCNodeInputTensors(const CNodePtr &CNode) {
auto tensorT = tmp_meta_graph->allTensors.at(input_index).get();
auto tensor_shape = tensorT->dims;
auto lite_tensor =
new (std::nothrow) Tensor(TypeId(tensorT->dataType), tensor_shape, tensorT->format, tensorT->nodeType);
new (std::nothrow) Tensor(TypeId(tensorT->dataType), tensor_shape, tensorT->format, tensorT->nodeType);
if (lite_tensor == nullptr) {
MS_LOG(ERROR) << "lite tensor is nullptr";
return input_tensors;
@ -106,7 +106,7 @@ kernel::LiteKernel *GetLiteKernel(std::vector<Tensor *> inputs, std::vector<Tens
mindspore::lite::PrimitiveC *primitive) {
MS_ASSERT(nullptr != lite_primitive);
auto data_type = inputs.front()->data_type();
kernel::KernelKey desc{kernel::KERNEL_ARCH::kCPU, data_type, (schema::PrimitiveType)primitive->Type()};
kernel::KernelKey desc{kernel::KERNEL_ARCH::kCPU, data_type, (schema::PrimitiveType) primitive->Type()};
lite::Context context;
auto creator = lite::KernelRegistry::GetInstance()->GetCreator(desc);
if (creator != nullptr) {
@ -115,6 +115,44 @@ kernel::LiteKernel *GetLiteKernel(std::vector<Tensor *> inputs, std::vector<Tens
}
return nullptr;
}
lite::STATUS ReplaceCNode(const FuncGraphPtr &func_graph, const CNodePtr &any_node, const AnfNodePtr &input_node,
std::vector<Tensor *> output_tensors, size_t replace_index) {
MS_ASSERT(func_graph != nullptr);
auto manager = func_graph->manager();
MS_ASSERT(manager != nullptr);
if (output_tensors.size() != 1) {
for (size_t k = 0; k < output_tensors.size(); k++) {
auto used_node_list = GetRealNodeUsedListByOutputIdx(func_graph, input_node, k);
if (used_node_list->size() != 1) {
MS_LOG(ERROR) << " output must tuple_getitem";
return lite::RET_ERROR;
}
auto tuple_node = used_node_list->at(0).first;
if (GetCNodeType(tuple_node) == schema::PrimitiveType_TupleGetItem) {
auto new_parameter = CreateNewParamter(func_graph, output_tensors.at(k));
if (new_parameter == nullptr) {
MS_LOG(ERROR) << "CreateNewParamter failed, name: " << input_node->fullname_with_scope();
return lite::RET_ERROR;
}
new_parameter->set_name(input_node->fullname_with_scope() + "_const_" + std::to_string(k));
manager->Replace(tuple_node, new_parameter);
} else {
MS_LOG(ERROR) << " multi out tensor must connect tuple-getitem: " << input_node->fullname_with_scope();
return lite::RET_ERROR;
}
}
} else {
auto new_parameter = CreateNewParamter(func_graph, output_tensors.front());
if (new_parameter == nullptr) {
MS_LOG(ERROR) << "CreateNewParamter failed, name: " << input_node->fullname_with_scope();
return lite::RET_ERROR;
}
new_parameter->set_name(input_node->fullname_with_scope());
any_node->set_input(replace_index, new_parameter);
}
return lite::RET_OK;
}
} // namespace
void FreeTensors(std::vector<Tensor *> *input_tensor, std::vector<Tensor *> *output_tensor) {
if (input_tensor != nullptr) {
@ -140,64 +178,66 @@ const AnfNodePtr ConstFoldPass::Process(const FuncGraphPtr &func_graph, const An
}
auto any_node = node->cast<CNodePtr>();
CheckIfCNodeIsNull(any_node);
bool changed = false;
for (size_t i = 1; i < any_node->inputs().size(); i++) {
auto input_node = any_node->input(i);
if (input_node->isa<CNode>() && CheckIsAllInputsParam(input_node)) {
auto input_cnode = input_node->cast<CNodePtr>();
auto input_tensors = GetCNodeInputTensors(input_cnode);
if (input_tensors.empty() || input_tensors.size() != input_cnode->inputs().size() - 1) {
FreeTensors(&input_tensors, nullptr);
continue;
if (!input_node->isa<CNode>() || !CheckIsAllInputsParam(input_node)) {
continue;
}
auto input_cnode = input_node->cast<CNodePtr>();
auto input_tensors = GetCNodeInputTensors(input_cnode);
if (input_tensors.empty() || input_tensors.size() != input_cnode->inputs().size() - 1) {
FreeTensors(&input_tensors, nullptr);
continue;
}
changed = true;
MS_LOG(INFO) << "Begin fold node:" << input_node->fullname_with_scope();
auto output_nums = GetOutputTensorNum(input_cnode);
std::vector<Tensor *> output_tensors{output_nums, new Tensor()};
auto lite_primitive = GetValueNode<std::shared_ptr<PrimitiveC>>(input_cnode->input(0));
if (lite_primitive == nullptr) {
MS_LOG(ERROR) << "lite_primitive is nullptr";
FreeTensors(&input_tensors, &output_tensors);
return nullptr;
}
// here, input_tensor's format need to be transposed nhwc according to fmkType,
// but for the time being, we only transpose the tensor with 0/1/2/3D.
// Others should be added in future.
for (size_t j = 0; j < input_tensors.size(); ++j) {
input_tensors[j]->SetFormat(schema::Format_NHWC);
if (input_tensors[j]->shape().size() == 4) {
MS_LOG(INFO) << "init input_tensor format to nhwc";
}
MS_LOG(INFO) << "Begin fold node:" << input_node->fullname_with_scope();
auto output_nums = GetOutputTensorNum(input_cnode);
std::vector<Tensor *> output_tensors{output_nums, new Tensor()};
auto lite_primitive = GetValueNode<std::shared_ptr<PrimitiveC>>(input_cnode->input(0));
if (lite_primitive == nullptr) {
MS_LOG(ERROR) << "lite_primitive is nullptr";
FreeTensors(&input_tensors, &output_tensors);
return nullptr;
}
// here, input_tensor's format need to be transposed nhwc according to fmkType,
// but for the time being, we only transpose the tensor with 0/1/2/3D.
// Others should be added in future.
for (size_t j = 0; j < input_tensors.size(); ++j) {
input_tensors[j]->SetFormat(schema::Format_NHWC);
if (input_tensors[j]->shape().size() == 4) {
MS_LOG(INFO) << "init input_tensor format to nhwc";
}
}
lite_primitive->InferShape(input_tensors, output_tensors);
auto parameter = kernel::PopulateParameter(lite_primitive.get());
if (parameter == nullptr) {
MS_LOG(ERROR) << "PopulateParameter return nullptr, type: "
<< schema::EnumNamePrimitiveType((schema::PrimitiveType)(lite_primitive->Type()));
return nullptr;
}
auto lite_kernel = GetLiteKernel(input_tensors, output_tensors, parameter, lite_primitive.get());
if (lite_kernel == nullptr) {
MS_LOG(ERROR) << "constant_folding schedule node lite kernel nullptr";
FreeTensors(&input_tensors, &output_tensors);
return nullptr;
}
auto ret = lite_kernel->Run();
if (0 != ret) {
FreeTensors(&input_tensors, &output_tensors);
MS_LOG(ERROR) << "run kernel failed, name: " << lite_kernel->name();
return nullptr;
}
auto new_parameter = CreateNewParamter(func_graph, output_tensors.front());
if (new_parameter == nullptr) {
FreeTensors(&input_tensors, &output_tensors);
MS_LOG(ERROR) << "CreateNewParamter failed, name: " << lite_kernel->name();
return nullptr;
}
new_parameter->set_name(input_node->fullname_with_scope());
any_node->set_input(i, new_parameter);
}
lite_primitive->InferShape(input_tensors, output_tensors);
auto parameter = kernel::PopulateParameter(lite_primitive.get());
if (parameter == nullptr) {
MS_LOG(ERROR) << "PopulateParameter return nullptr, type: "
<< schema::EnumNamePrimitiveType((schema::PrimitiveType) (lite_primitive->Type()));
return nullptr;
}
auto lite_kernel = GetLiteKernel(input_tensors, output_tensors, parameter, lite_primitive.get());
if (lite_kernel == nullptr) {
MS_LOG(ERROR) << "constant_folding schedule node lite kernel nullptr";
FreeTensors(&input_tensors, &output_tensors);
return nullptr;
}
auto ret = lite_kernel->Run();
if (0 != ret) {
FreeTensors(&input_tensors, &output_tensors);
MS_LOG(ERROR) << "run kernel failed, name: " << lite_kernel->name();
return nullptr;
}
// replace cnode by new param
if (ReplaceCNode(func_graph, any_node, input_node, output_tensors, i) != lite::RET_OK) {
FreeTensors(&input_tensors, &output_tensors);
delete (lite_kernel);
MS_LOG(ERROR) << "constant_folding replace cnode failed";
return nullptr;
}
FreeTensors(&input_tensors, &output_tensors);
delete (lite_kernel);
}
return any_node;
return changed ? any_node : nullptr;
}
} // namespace mindspore::opt