Files
paddlepaddle--paddle/paddle/phi/core/kernel_factory.cc
T
2026-07-13 12:40:42 +08:00

644 lines
22 KiB
C++

// Copyright (c) 2021 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.
#include "paddle/phi/core/kernel_factory.h"
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/core/enforce.h"
#if defined(PADDLE_WITH_XPU)
#include "paddle/phi/backends/xpu/xpu_op_list.h"
#include "paddle/phi/common/data_type.h"
#include "paddle/phi/core/compat/convert_utils.h"
#endif
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
#include "paddle/phi/backends/custom/custom_device_op_list.h"
#endif
#include "paddle/phi/core/compat/op_utils.h"
#include "paddle/utils/string/string_helper.h"
PHI_DEFINE_EXPORTED_bool(use_stride_kernel,
true,
"Whether to use stride kernel if op support stride.");
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(enable_api_kernel_fallback);
PD_DECLARE_bool(run_kp_kernel);
namespace phi {
const static Kernel empty_kernel; // NOLINT
std::string KernelSelectionErrorMessage(const std::string& kernel_name,
const KernelKey& target_key);
uint32_t KernelKey::Hash::operator()(const KernelKey& key) const {
uint32_t hash_value = 0;
// |----31-20------|---19-12---|---11-8----|---7-0---|
// | For extension | DataType | DataLayout | Backend |
hash_value |= static_cast<uint8_t>(key.backend());
hash_value |=
(static_cast<uint8_t>(key.layout()) << KernelKey::kBackendBitLength);
hash_value |=
(static_cast<uint16_t>(key.dtype())
<< (KernelKey::kBackendBitLength + KernelKey::kDataLayoutBitLength));
return hash_value;
}
KernelFactory& KernelFactory::Instance() {
static KernelFactory g_op_kernel_factory;
return g_op_kernel_factory;
}
bool KernelFactory::HasCompatiblePhiKernel(const std::string& op_type) const {
if (phi::OpUtilsMap::Instance().Contains(op_type) ||
(kernels_.find(op_type) != kernels_.end())) {
return true;
}
return false;
}
bool KernelFactory::HasStructuredKernel(const std::string& op_type) const {
auto phi_kernel_name = phi::OpUtilsMap::Instance().GetBaseKernelName(op_type);
auto kernel_iter = kernels_.find(phi_kernel_name);
if (kernel_iter != kernels_.end()) {
return std::any_of(kernel_iter->second.begin(),
kernel_iter->second.end(),
[](phi::KernelKeyMap::const_reference kernel_pair) {
return kernel_pair.second.GetKernelRegisteredType() ==
KernelRegisteredType::STRUCTURE;
});
}
return false;
}
const Kernel& KernelFactory::SelectKernel(const std::string& kernel_name,
const KernelKey& kernel_key) const {
auto iter = kernels_.find(kernel_name);
if (iter == kernels_.end()) {
return empty_kernel;
}
auto kernel_iter = iter->second.find(kernel_key);
if (kernel_iter == iter->second.end() &&
kernel_key.layout() != phi::DataLayout::ALL_LAYOUT) {
phi::KernelKey any_layout_kernel_key(
kernel_key.backend(), phi::DataLayout::ALL_LAYOUT, kernel_key.dtype());
kernel_iter = iter->second.find(any_layout_kernel_key);
}
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
if (kernel_iter == iter->second.end() &&
kernel_key.backend() > phi::Backend::NUM_BACKENDS) {
kernel_iter = iter->second.find({phi::Backend::CUSTOM,
phi::DataLayout::ALL_LAYOUT,
kernel_key.dtype()});
}
#endif
if (kernel_iter == iter->second.end()) {
return empty_kernel;
}
return kernel_iter->second;
}
const Kernel& KernelFactory::SelectKernelWithGPUDNN(
const std::string& kernel_name, const KernelKey& const_kernel_key) const {
auto iter = kernels_.find(kernel_name);
if (iter == kernels_.end()) {
return empty_kernel;
}
KernelKey kernel_key = KernelKey(const_kernel_key);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (kernel_key.backend() == Backend::GPUDNN) {
auto kernel_iter = iter->second.find(
{Backend::GPUDNN, phi::DataLayout::ALL_LAYOUT, kernel_key.dtype()});
if (kernel_iter != iter->second.end()) {
return kernel_iter->second;
}
kernel_key =
KernelKey(Backend::GPU, kernel_key.layout(), kernel_key.dtype());
}
#endif
auto kernel_iter = iter->second.find(kernel_key);
if (kernel_iter == iter->second.end() &&
kernel_key.layout() != phi::DataLayout::ALL_LAYOUT) {
phi::KernelKey any_layout_kernel_key(
kernel_key.backend(), phi::DataLayout::ALL_LAYOUT, kernel_key.dtype());
kernel_iter = iter->second.find(any_layout_kernel_key);
}
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
if (kernel_iter == iter->second.end() &&
kernel_key.backend() > phi::Backend::NUM_BACKENDS) {
kernel_iter = iter->second.find({phi::Backend::CUSTOM,
phi::DataLayout::ALL_LAYOUT,
kernel_key.dtype()});
}
#endif
if (kernel_iter == iter->second.end()) {
return empty_kernel;
}
return kernel_iter->second;
}
KernelKeyMap KernelFactory::SelectKernelMap(
const std::string& kernel_name) const {
auto iter = kernels_.find(kernel_name);
if (iter == kernels_.end()) {
return KernelKeyMap();
}
return iter->second;
}
bool KernelFactory::HasKernel(const std::string& kernel_name,
const KernelKey& kernel_key) const {
auto iter = kernels_.find(kernel_name);
PADDLE_ENFORCE_NE(iter,
kernels_.end(),
common::errors::NotFound(
"The kernel `%s` is not registered.", kernel_name));
auto kernel_iter = iter->second.find(kernel_key);
if (kernel_iter == iter->second.end() &&
kernel_key.layout() != phi::DataLayout::ALL_LAYOUT) {
phi::KernelKey any_layout_kernel_key(
kernel_key.backend(), phi::DataLayout::ALL_LAYOUT, kernel_key.dtype());
kernel_iter = iter->second.find(any_layout_kernel_key);
}
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
if (kernel_iter == iter->second.end() &&
kernel_key.backend() > phi::Backend::NUM_BACKENDS) {
kernel_iter = iter->second.find({phi::Backend::CUSTOM,
phi::DataLayout::ALL_LAYOUT,
kernel_key.dtype()});
}
#endif
if (kernel_iter == iter->second.end()) {
return false;
}
if (kernel_key.backend() == Backend::XPU) {
#if defined(PADDLE_WITH_XPU_KP)
auto fluid_op_name = TransToFluidOpName(kernel_name);
bool has_kp_kernel = false;
VLOG(6) << "fluid_op_name: " << TransToFluidOpName(kernel_name);
bool is_xpu_kp_supported = phi::backends::xpu::is_xpu_kp_support_op(
fluid_op_name, kernel_key.dtype());
// Check in xpu_kp
if (is_xpu_kp_supported && FLAGS_run_kp_kernel) {
auto kernel_key_kp =
KernelKey(Backend::KPS, kernel_key.layout(), kernel_key.dtype());
auto kernel_iter_kp = iter->second.find(kernel_key_kp);
has_kp_kernel = (kernel_iter_kp != iter->second.end());
if (has_kp_kernel) {
kernel_key = kernel_key_kp;
kernel_iter = kernel_iter_kp;
}
}
// check in xpu
bool xpu_unsupported = !phi::backends::xpu::is_xpu_support_op(
fluid_op_name, kernel_key.dtype());
VLOG(6) << "Current KernelKey is " << kernel_key;
// Fall back to CPU, when FLAGS_enable_api_kernel_fallback is true and op
// was unregistered in xpu and kp
if (FLAGS_enable_api_kernel_fallback &&
(kernel_iter == iter->second.end() ||
(xpu_unsupported && !has_kp_kernel))) {
return false;
}
#elif defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
VLOG(6) << "fluid_op_name: " << TransToFluidOpName(kernel_name);
if ((FLAGS_enable_api_kernel_fallback &&
kernel_iter == iter->second.end()) ||
!phi::backends::xpu::is_xpu_support_op(TransToFluidOpName(kernel_name),
kernel_key.dtype())) {
return false;
}
#endif
}
return true;
}
void KernelFactory::AddToLowPrecisionKernelList(
const std::string& name, const DataType& kernel_key_type) {
if (FLAGS_low_precision_op_list >= 1) {
auto op_name = phi::TransToFluidOpName(name);
if (op_name.find("_grad") != std::string::npos) {
return; // only record forward api
}
if (low_precision_kernels_.find(op_name) == low_precision_kernels_.end()) {
auto count = OpCount();
low_precision_kernels_[op_name] = count;
}
if (kernel_key_type == DataType::FLOAT16) {
low_precision_kernels_[op_name].fp16_called_ += 1;
} else if (kernel_key_type == DataType::BFLOAT16) {
low_precision_kernels_[op_name].bf16_called_ += 1;
} else if (kernel_key_type == DataType::FLOAT32) {
low_precision_kernels_[op_name].fp32_called_ += 1;
} else {
low_precision_kernels_[op_name].other_called_ += 1;
}
}
}
std::map<const std::string, OpCount>
KernelFactory::GetLowPrecisionKernelList() {
return low_precision_kernels_;
}
KernelResult KernelFactory::SelectKernelOrThrowError(
const std::string& kernel_name,
const KernelKey& const_kernel_key,
bool use_strided_kernel) const {
auto iter = kernels_.find(kernel_name);
PADDLE_ENFORCE_NE(iter,
kernels_.end(),
common::errors::NotFound(
"The kernel `%s` is not registered.", kernel_name));
if (FLAGS_use_stride_kernel && use_strided_kernel) {
auto stride_kernel_iter = iter->second.find(
{const_kernel_key.backend() == paddle::experimental::Backend::GPUDNN
? paddle::experimental::get_accelerat_backend()
: const_kernel_key.backend(),
phi::DataLayout::STRIDED,
const_kernel_key.dtype()});
if (stride_kernel_iter != iter->second.end()) {
return {stride_kernel_iter->second, false, true};
}
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (stride_kernel_iter == iter->second.end() &&
(const_kernel_key.backend() > phi::Backend::NUM_BACKENDS ||
const_kernel_key.backend() == phi::Backend::GPUDNN)) {
stride_kernel_iter = iter->second.find({phi::Backend::CUSTOM,
phi::DataLayout::STRIDED,
const_kernel_key.dtype()});
if (stride_kernel_iter != iter->second.end()) {
return {stride_kernel_iter->second, false, true};
}
}
#endif
}
KernelKey kernel_key = KernelKey(const_kernel_key.backend(),
phi::DataLayout::ALL_LAYOUT,
const_kernel_key.dtype());
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
defined(PADDLE_WITH_CUSTOM_DEVICE)
if (kernel_key.backend() == Backend::GPUDNN) {
auto kernel_iter = iter->second.find(
{Backend::GPUDNN, phi::DataLayout::ALL_LAYOUT, kernel_key.dtype()});
if (kernel_iter != iter->second.end()) {
return {kernel_iter->second, false, false};
}
kernel_key = KernelKey(paddle::experimental::get_accelerat_backend(),
kernel_key.layout(),
kernel_key.dtype());
}
#endif
auto kernel_iter = iter->second.find(kernel_key);
PADDLE_ENFORCE_NE(
kernel_iter == iter->second.end() && kernel_key.backend() == Backend::CPU,
true,
common::errors::NotFound(
"The kernel with key %s of kernel `%s` is not registered. %s",
kernel_key,
kernel_name,
KernelSelectionErrorMessage(kernel_name, kernel_key)));
#if defined(PADDLE_WITH_XPU_KP)
auto fluid_op_name = TransToFluidOpName(kernel_name);
bool has_kp_kernel = false;
VLOG(6) << "fluid_op_name: " << TransToFluidOpName(kernel_name);
bool is_xpu_kp_supported = phi::backends::xpu::is_xpu_kp_support_op(
fluid_op_name, kernel_key.dtype());
// Check in xpu_kp
if (is_xpu_kp_supported && FLAGS_run_kp_kernel) {
auto kernel_key_kp =
KernelKey(Backend::KPS, kernel_key.layout(), kernel_key.dtype());
auto kernel_iter_kp = iter->second.find(kernel_key_kp);
has_kp_kernel = (kernel_iter_kp != iter->second.end());
if (has_kp_kernel) {
kernel_key = kernel_key_kp;
kernel_iter = kernel_iter_kp;
}
}
// check in xpu
bool xpu_unsupported =
!phi::backends::xpu::is_xpu_support_op(fluid_op_name, kernel_key.dtype());
VLOG(6) << "Current KernelKey is " << kernel_key;
// Fall back to CPU, when FLAGS_enable_api_kernel_fallback is true and op
// was unregistered in xpu and kp
if (FLAGS_enable_api_kernel_fallback &&
(kernel_iter == iter->second.end() || (xpu_unsupported && !has_kp_kernel))
#elif defined(PADDLE_WITH_XPU) && !defined(PADDLE_WITH_XPU_KP)
VLOG(6) << "fluid_op_name: " << TransToFluidOpName(kernel_name);
bool is_xpu_unsupported =
kernel_key.backend() == Backend::XPU &&
!phi::backends::xpu::is_xpu_support_op(TransToFluidOpName(kernel_name),
kernel_key.dtype()) &&
!phi::backends::xpu::is_xpu_support_op(kernel_name, kernel_key.dtype());
if ((FLAGS_enable_api_kernel_fallback && kernel_iter == iter->second.end()) ||
is_xpu_unsupported
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
if (kernel_iter == iter->second.end() &&
kernel_key.backend() > phi::Backend::NUM_BACKENDS) {
kernel_iter = iter->second.find({phi::Backend::CUSTOM,
phi::DataLayout::ALL_LAYOUT,
kernel_key.dtype()});
}
if (FLAGS_enable_api_kernel_fallback &&
(kernel_iter == iter->second.end() ||
phi::backends::custom_device::is_in_custom_black_list(
TransToFluidOpName(kernel_name)))
#else
if ((FLAGS_enable_api_kernel_fallback && kernel_iter == iter->second.end())
#endif
) {
// Fallback CPU backend
phi::KernelKey cpu_kernel_key(
phi::Backend::CPU, kernel_key.layout(), kernel_key.dtype());
kernel_iter = iter->second.find(cpu_kernel_key);
PADDLE_ENFORCE_NE(
kernel_iter,
iter->second.end(),
common::errors::NotFound(
"The kernel with key %s of kernel `%s` is not registered and "
"fail to fallback to CPU one. %s",
kernel_key,
kernel_name,
KernelSelectionErrorMessage(kernel_name, kernel_key)));
VLOG(1) << "missing " << kernel_key.backend() << " kernel: " << kernel_name
<< ", expected_kernel_key:" << kernel_key
<< ", fallbacking to CPU one!";
return {kernel_iter->second, true, false};
}
PADDLE_ENFORCE_NE(
kernel_iter,
iter->second.end(),
common::errors::NotFound(
"The kernel with key %s of kernel `%s` is not registered. %s "
"The current value of FLAGS_enable_api_kernel_fallback(bool,"
" default true) is false. If you want to fallback this kernel"
" to CPU one, please set the flag true before run again.",
kernel_key,
kernel_name,
KernelSelectionErrorMessage(kernel_name, kernel_key)));
return {kernel_iter->second, false, false};
}
const KernelArgsDef& KernelFactory::GetFirstKernelArgsDef(
const std::string& kernel_name) const {
auto iter = kernels_.find(kernel_name);
PADDLE_ENFORCE_NE(iter,
kernels_.end(),
common::errors::NotFound(
"The kernel `%s` is not registered.", kernel_name));
return iter->second.cbegin()->second.args_def();
}
std::ostream& operator<<(std::ostream& os, AttributeType attr_type) {
switch (attr_type) {
case AttributeType::BOOL:
os << "bool";
break;
case AttributeType::INT32:
os << "int";
break;
case AttributeType::INT64:
os << "int64_t";
break;
case AttributeType::FLOAT32:
os << "float";
break;
case AttributeType::FLOAT64:
os << "double";
break;
case AttributeType::STRING:
os << "string";
break;
case AttributeType::BOOLS:
os << "vector<bool>";
break;
case AttributeType::INT32S:
os << "vector<int>";
break;
case AttributeType::INT64S:
os << "vector<int64_t>";
break;
case AttributeType::FLOAT32S:
os << "vector<float>";
break;
case AttributeType::FLOAT64S:
os << "vector<double>";
break;
case AttributeType::STRINGS:
os << "vector<string>";
break;
case AttributeType::SCALAR:
os << "Scalar";
break;
case AttributeType::SCALARS:
os << "vector<Scalar>";
break;
case AttributeType::INT_ARRAY:
os << "IntArray";
break;
case AttributeType::DATA_TYPE:
os << "DataType";
break;
case AttributeType::DATA_LAYOUT:
os << "DataLayout";
break;
case AttributeType::PLACE:
os << "Place";
break;
default:
os << "Undefined";
}
return os;
}
// print kernel info with json format:
// {
// "(CPU, Undefined(AnyLayout), complex64)": {
// "input": ["CPU, NCHW, complex64", "CPU, NCHW, complex64"],
// "output": ["CPU, NCHW, complex64"],
// "attribute": ["i"]
// }
std::ostream& operator<<(std::ostream& os, const Kernel& kernel) {
// input
os << "{\"input\":[";
bool need_comma = false;
for (auto& in_def : kernel.args_def().input_defs()) {
if (need_comma) os << ",";
os << "\n\tbackend: " << in_def.backend << ", "
<< " layout: " << in_def.layout << ", "
<< " dtype: " << in_def.dtype;
need_comma = true;
}
os << "\n],";
// output
os << "\n\"output\":[";
need_comma = false;
for (auto& out_def : kernel.args_def().output_defs()) {
if (need_comma) os << ",";
os << "\n\tbackend: " << out_def.backend << ", "
<< " layout: " << out_def.layout << ", "
<< " dtype: " << out_def.dtype;
need_comma = true;
}
os << "\n],";
// attr
os << "\n\"attribute\":[";
need_comma = false;
for (auto& arg_def : kernel.args_def().attribute_defs()) {
if (need_comma) os << ",";
os << "\n\t\"" << arg_def.type_index << "\"";
need_comma = true;
}
os << "\n]}";
return os;
}
// print all kernels info with json format:
// {
// "kernel_name1":
// [
// {
// "(CPU, Undefined(AnyLayout), complex64)": {
// "input": ["CPU, NCHW, complex64", "CPU, NCHW, complex64"],
// "output": ["CPU, NCHW, complex64"],
// "attribute": ["i"]
// },
// ...
// ],
// "kernel_name2": []
// ...
// }
std::ostream& operator<<(std::ostream& os, KernelFactory& kernel_factory) {
os << "{";
bool need_comma_kernels = false;
for (const auto& op_kernel_pair : kernel_factory.kernels()) {
if (need_comma_kernels) {
os << ",";
os << std::endl;
}
os << "\"" << op_kernel_pair.first << " \":[" << std::endl;
bool need_comma_per_kernel = false;
for (const auto& kernel_pair : op_kernel_pair.second) {
if (need_comma_per_kernel) {
os << ",";
os << std::endl;
}
os << "{\"" << kernel_pair.first << "\":" << kernel_pair.second << "}";
need_comma_per_kernel = true;
}
os << "]";
need_comma_kernels = true;
}
os << "}";
return os;
}
// return all kernel selection error message of specific kernel_name:
// 1. If target_key not supports target backend, output "Selected wrong Backend
// ..."
// 2. If target_key not supports target datatype, output "Selected wrong
// DataType ..."
// 3. `target_key` is still not supported, output all kernel keys of
// corresponding kernel_name:
// {
// (CPU, NCHW, [int8, int16, ...]);
// (GPU, Undefined(AnyLayout), [float32, float64, ...]);
// ...
// }
std::string KernelSelectionErrorMessage(const std::string& kernel_name,
const KernelKey& target_key) {
PADDLE_ENFORCE_NE(KernelFactory::Instance().kernels().find(kernel_name),
KernelFactory::Instance().kernels().end(),
common::errors::NotFound(
"The kernel `%s` is not registered.", kernel_name));
// Init data structure
bool support_backend = false;
bool support_dtype = false;
std::unordered_map<std::string, std::vector<std::string>> all_kernel_key;
std::unordered_set<std::string> backend_set;
std::unordered_set<std::string> dtype_set;
// Record all kernel information of kernel_name
for (auto const& iter : KernelFactory::Instance().kernels()[kernel_name]) {
KernelKey kernel_key = iter.first;
if (kernel_key.backend() == target_key.backend()) {
support_backend = true;
if (kernel_key.dtype() == target_key.dtype()) {
support_dtype = true;
}
dtype_set.insert(DataTypeToString(kernel_key.dtype()));
}
backend_set.insert(
paddle::experimental::BackendToString(kernel_key.backend()));
all_kernel_key[paddle::experimental::BackendToString(kernel_key.backend()) +
", " + common::DataLayoutToString(kernel_key.layout())]
.push_back(DataTypeToString(kernel_key.dtype()));
}
// 1. If target_key not supports target backend, output "Selected wrong
// Backend ..."
if (!support_backend) {
std::string error_message = paddle::string::join_strings(backend_set, ", ");
return "Selected wrong Backend `" +
paddle::experimental::BackendToString(target_key.backend()) +
"`. Paddle support following Backends: " + error_message + ".";
}
// 2. If target_key not supports target datatype, output "Selected wrong
// DataType ..."
if (!support_dtype) {
std::string error_message = paddle::string::join_strings(dtype_set, ", ");
return "Selected wrong DataType `" + DataTypeToString(target_key.dtype()) +
"`. Paddle support following DataTypes: " + error_message + ".";
}
// 3. `target_key` is still not supported, output all kernel keys of
// corresponding kernel_name
std::string message = "Currently, paddle support following kernel keys of `" +
kernel_name + "`: { ";
for (auto& item : all_kernel_key) {
std::vector<std::string>& dtype_vec = item.second;
message += "(" + item.first + ", [";
message += paddle::string::join_strings(dtype_vec, ", ");
message += "]); ";
}
message += "}.";
return message;
}
} // namespace phi