Files
paddlepaddle--paddle/paddle/phi/kernels/fusion/cutlass/conv2d/conv2d_common.py
T
2026-07-13 12:40:42 +08:00

244 lines
7.5 KiB
Python

# Copyright (c) 2023 PaddlePaddle Authors. 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.
import os
import sys
dirname, filename = os.path.split(os.path.abspath(sys.argv[0]))
sys.path.append(dirname + "/../")
from util import SubstituteTemplate
# For beginners, these template parameters may be difficult to understand.
# Please refer to the conv-related demo of CUTLASS for better understanding.
# https://github.com/NVIDIA/cutlass/tree/master/examples
CommonCutlassConvKernelDeclare = """
cutlass::Status ${kernel_func_name}(const ConvAllParams& params) {
using kernel_base =
typename cutlass::conv::kernel::${conv_kind_name}<
${element_a},
${layout_a},
${element_b},
${layout_b},
${element_c},
${layout_c},
${element_accum},
${opcode_class},
${arch},
cutlass::gemm::GemmShape<${Tshape}>,
cutlass::gemm::GemmShape<${Wshape}>,
cutlass::gemm::GemmShape<${Ishape}>,
${epi_part},
${swizzling_functor},
${stages},
${math_operator},
${iterator_algorithm},
${stride_support},
${align_a},
${align_b}
>::Kernel;
using ImplicitGemm =
cutlass::conv::device::ImplicitGemmConvolution<kernel_base>;
${element_a} *input = (${element_a} *)(params.input);
${element_b} *weight = (${element_b} *)(params.weight);
${element_c} *bias = (${element_c} *)(params.bias);
${element_c} *output = (${element_c} *)(params.output);
// only used by conv2d_bias_residual
auto residual = (${element_c} *)(params.residual);
int batch = params.batch;
int ic = params.ic;
int ih = params.ih;
int iw = params.iw;
int kh = params.kh;
int kw = params.kw;
int oc = params.oc;
int pad_h0 = params.pad_h0;
int pad_w0 = params.pad_w0;
int stride_h = params.stride_h;
int stride_w = params.stride_w;
int groups = params.groups;
int kc = ic / groups;
int oh = params.oh;
int ow = params.ow;
int dilation_h = params.dilation_h;
int dilation_w = params.dilation_w;
int split_k_slices = ${split_k_slices};
cutlass::conv::Conv2dProblemSize problem_size({batch, ih, iw, ic},
{oc, kh, kw, ic / groups},
{pad_h0, 0, pad_w0, 0},
{stride_h, stride_w},
{dilation_h, dilation_w},
{batch, oh, ow, oc},
cutlass::conv::Mode::kCrossCorrelation,
split_k_slices,
groups);
"""
# This is the execution part of this cutlass conv kernel.
CommonCutlassConvKernelExecute = """
ImplicitGemm implicit_gemm_op;
size_t bytes = implicit_gemm_op.get_workspace_size(arguments);
auto stream = params.stream;
void *workspace = params.workspace;
cutlass::Status status = implicit_gemm_op.can_implement(arguments);
CUTLASS_CHECK(status);
status = implicit_gemm_op.initialize(arguments, workspace);
CUTLASS_CHECK(status);
status = implicit_gemm_op(stream);
CUTLASS_CHECK(status);
return status;
}
"""
# CommonConvFunction is a wrapper for many kernels
# a func_name is like conv2d_bias_silu_sm75
# it has many kernels, we should pick up a performance-best
# ${func_name} is like conv2d_bias_silu_sm75
# ${enum_op_name} is like CONV2D_BIAS_SILU
CommonConvFunction = """
${kernel_func_declare}
std::vector<std::function<cutlass::Status(const ConvAllParams)>>
${func_name}_all_func = {${all_kernel_func_name}};
std::map<std::vector<int>, int> map_problem_${func_name};
std::mutex ${func_name}_mutex;
bool ${func_name}(ConvAllParams params) {
int batch = params.batch;
int ic = params.ic;
int ih = params.ih;
int iw = params.iw;
int kh = params.kh;
int kw = params.kw;
int oc = params.oc;
//int pad_h0 = params.pad_h0;
//int pad_w0 = params.pad_w0;
int groups = params.groups;
int stride_h = params.stride_h;
int stride_w = params.stride_w;
std::vector<int> problem_size = {
batch, ic, ih, iw, kh, kw, oc, groups, stride_h, stride_w};
if (map_problem_${func_name}.count(problem_size)) {
${func_name}_all_func[map_problem_${func_name}.at(problem_size)](
params);
return true;
}
int best_config_index = ProfileToGetBestConfig(
${func_name}_all_func, params, ${enum_op_name});
std::lock_guard<std::mutex> guard(${func_name}_mutex);
map_problem_${func_name}[problem_size] = best_config_index;
${func_name}_all_func[best_config_index](params);
return true;
}
"""
# We should wrapper all op_name_with_sm_version into a function
# like : wrapper conv2d_bias_silu_sm75, conv2d_bias_silu_sm80, conv2d_bias_silu_sm86 into conv2d_bias_silu for phi kernel
# this function is invoked by phi kernel
CommonWrapperForPhi = """
bool ${op_name}(ConvAllParams params) {
${dispatch_body}
}
"""
def convert_c_data_type(dtype):
if dtype == "fp16":
return "Conv2dDataType::fp16"
elif dtype == "bf16":
return "Conv2dDataType::bf16"
elif dtype == "fp32":
return "Conv2dDataType::fp32"
else:
return None
CommonDispatchTemp = '''
if (params.sm_version == ${sm_code} && params.data_type == ${data_type})
{
return ${op_name_with_sm}(params);
}
'''
# this is a file's ending part
CommonTail = '''
} // namespace cutlass_internal
} // namespace fusion
} // namespace phi
'''
# Wrap different sm versions into a function called by phi
def GenerateFunctionForPhi(
sm_versions_and_types, support_epi_funcs, underscore_names, camel_names
):
generated_code = ""
for epi_func in support_epi_funcs:
dispatch_body = ""
for sm_version, data_type in sm_versions_and_types:
sm_dicts = {}
sm_dicts["sm_code"] = sm_version
sm_dicts["data_type"] = convert_c_data_type(data_type)
sm_dicts["op_name_with_sm"] = (
underscore_names[epi_func].lower()
+ "_sm"
+ sm_version
+ "_"
+ data_type
)
dispatch_body += SubstituteTemplate(CommonDispatchTemp, sm_dicts)
dispatch_body += ''' return false;'''
op_dicts = {}
op_dicts["dispatch_body"] = dispatch_body
op_dicts["op_name"] = camel_names[epi_func]
generated_code += SubstituteTemplate(CommonWrapperForPhi, op_dicts)
return generated_code
# We modify some template parameters based on CommonCutlassConvKernelDeclare.
CommonCutlassConv2dDepthwiseKernelDeclare = (
CommonCutlassConvKernelDeclare.replace(
"${align_a}", "cutlass::MatrixShape<${strided_shape}>"
)
.replace("${align_b}", "cutlass::MatrixShape<${dilation_shape}>")
.replace("ImplicitGemmConvolution", "DirectConvolution")
.replace(
"cutlass::gemm::GemmShape<${Tshape}>,",
'''cutlass::gemm::GemmShape<${Tshape}>,
cutlass::conv::TensorNHWCShape<${T_output_shape}>,
cutlass::MatrixShape<${filter_shape}>,
''',
)
)