Files
alibaba--mnn/tools/converter/source/optimizer/merge/FuseSplitGeLu.cpp
T
2026-07-13 13:33:03 +08:00

185 lines
5.9 KiB
C++

//
// FuseSplitGeLu.cpp
// MNNConverter
//
// Created by MNN on 2024/01/08.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include <unordered_map>
#include "../TemplateMerge.hpp"
#include "MNN/expr/ExprCreator.hpp"
#include "MNN_generated.h"
#include "MergeHelpers.hpp"
namespace MNN {
namespace Express {
class FuseSplitGeLu {
public:
FuseSplitGeLu();
private:
VARP split_gelu_input_0;
VARP split_gelu_input_1;
bool mHasPrefixAdd = false;
};
bool IsUnaryErf(EXPRP expr) {
const Op* op = expr->get();
if (op == nullptr || op->type() != OpType_UnaryOp) {
return false;
}
return op->main_as_UnaryOp()->opType() == UnaryOpOperation_ERF;
}
FuseSplitGeLu::FuseSplitGeLu() {
auto match = [this](EXPRP expr) -> bool {
auto config = Global<modelConfig>::Get();
if(!config->transformerFuse) {
return false;
}
// mul
if (!expr->get() || !helpers::IsBinaryMul(expr)) {
return false;
}
EXPRP x, y, z;
EXPRP add_1, add_2;
EXPRP mul_1, mul_2, mul_3;
// mul
x = expr->inputs().at(0)->expr().first;
y = expr->inputs().at(1)->expr().first;
if (helpers::IsBinaryMul(x) && helpers::IsSlice(y)) {
z = x;
if(!helpers::IsConstant(y->inputs().at(1)->expr().first) || y->inputs().at(1)->readMap<int32_t>()[0] != 0) {
return false;
}
if(!helpers::IsConstant(y->inputs().at(3)->expr().first) || y->inputs().at(3)->readMap<int32_t>()[0] != -1) {
return false;
}
} else if (helpers::IsBinaryMul(y) && helpers::IsSlice(x)) {
z = y;
if(!helpers::IsConstant(x->inputs().at(1)->expr().first) || x->inputs().at(1)->readMap<int32_t>()[0] != 0) {
return false;
}
if(!helpers::IsConstant(x->inputs().at(3)->expr().first) || x->inputs().at(3)->readMap<int32_t>()[0] != -1) {
return false;
}
} else {
return false;
}
// x * a
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsBinaryMul(x) && helpers::IsConstant(y)) {
z = x;
} else if (helpers::IsBinaryMul(y) && helpers::IsConstant(x)) {
z = y;
} else {
return false;
}
// mul
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsBinaryAdd(x) && helpers::IsSlice(y)){
z = x;
} else if (helpers::IsBinaryAdd(y) && helpers::IsSlice(x)){
z = y;
} else {
return false;
}
// z -> x + a
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (IsUnaryErf(x) && helpers::IsConstant(y)){
z = x;
} else if (helpers::IsConstant(x) && IsUnaryErf(y)) {
z = y;
} else {
return false;
}
// z -> erf
x = z->inputs().at(0)->expr().first;
if (helpers::IsBinaryRealDiv(x)){
z = x;
} else {
return false;
}
// div (x / a)
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsSlice(x) && helpers::IsConstant(y)){
z = x;
} else {
return false;
}
// slice
x = z->inputs().at(0)->expr().first;
auto res = z;
mHasPrefixAdd = false;
if (helpers::IsBinaryAdd(x)) {
z = x;
x = z->inputs().at(0)->expr().first;
y = z->inputs().at(1)->expr().first;
if (helpers::IsConstant(x)){
if(z->inputs().at(0)->getInfo()->dim.size() == 1) {
split_gelu_input_0 = z->inputs().at(1);
split_gelu_input_1 = z->inputs().at(0);
} else {
split_gelu_input_0 = res->inputs().at(0);
return true;
}
} else if (helpers::IsConstant(y)){
if(z->inputs().at(1)->getInfo()->dim.size() == 1) {
split_gelu_input_0 = z->inputs().at(0);
split_gelu_input_1 = z->inputs().at(1);
} else {
split_gelu_input_0 = res->inputs().at(0);
return true;
}
} else {
split_gelu_input_0 = res->inputs().at(0);
return true;
}
mHasPrefixAdd = true;
} else {
split_gelu_input_0 = res->inputs().at(0);
}
return true;
};
auto fold = [this](EXPRP expr) -> bool {
auto config = Global<modelConfig>::Get();
auto version = config->targetVersion;
if (version < 2.8f) {
// For target version < 2.8 , don't support split_gelu
return false;
}
std::unique_ptr<OpT> split_gelu_op(new OpT);
split_gelu_op->name = expr->name();
split_gelu_op->type = OpType_SplitGeLU;
if (mHasPrefixAdd) {
auto split_gelu_expr = Variable::create(Expr::create(split_gelu_op.get(), {split_gelu_input_0, split_gelu_input_1}, 1));
split_gelu_expr->setName("SplitGeLU_" + expr->name());
Expr::replace(expr, split_gelu_expr->expr().first);
} else {
auto split_gelu_expr = Variable::create(Expr::create(split_gelu_op.get(), { split_gelu_input_0 }, 1));
split_gelu_expr->setName("SplitGeLU_" + expr->name());
Expr::replace(expr, split_gelu_expr->expr().first);
}
return true /*modified*/;
};
TemplateMerge::getInstance("Merge").insertTemplate("FuseSplitGeLu", match, fold);
}
//static FuseSplitGeLu g_fuse_splitgelu;
} // namespace Express
} // namespace MNN