Files
paddlepaddle--paddle/paddle/fluid/imperative/amp_utils.h
T
2026-07-13 12:40:42 +08:00

347 lines
12 KiB
C++

// Copyright (c) 2022 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.
#pragma once
#include <string>
#if !(defined(PADDLE_NO_PYTHON) && defined(PADDLE_ON_INFERENCE))
#include "paddle/fluid/eager/api/generated/eager_generated/forwards/dygraph_functions.h"
#endif
#include "paddle/fluid/eager/api/utils/global_utils.h"
#include "paddle/fluid/eager/type_defs.h"
#include "paddle/fluid/imperative/amp_auto_cast.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/phi/api/include/api.h"
#include "paddle/phi/api/include/sparse_api.h"
#include "paddle/utils/small_vector.h"
namespace paddle {
namespace imperative {
static inline DataType GetDataType(const pir::Value& value) {
return paddle::dialect::GetValueDataType(value);
}
static inline DataType GetDataType(const paddle::Tensor& tensor) {
return tensor.dtype();
}
template <class T>
static inline DataType GetPromoteType(
const std::string& op_name,
const paddle::small_vector<std::vector<T>, egr::kSlotSmallVectorSize>&
amp_tensors_vector,
const DataType& amp_dtype) {
auto dst_type = amp_dtype;
// only consider the dtype of input(X).
if (op_name == "batch_norm" || op_name == "layer_norm" ||
op_name == "sync_batch_norm" ||
op_name == "moving_average_abs_max_scale") {
if (GetDataType(amp_tensors_vector[0][0]) == DataType::FLOAT32) {
dst_type = DataType::FLOAT32;
}
return dst_type;
}
if (egr::Controller::Instance().GetCurrentTracer()->GetAmpDtype() ==
"float16") {
if (op_name == "fused_attention") {
for (size_t i = 0; i < amp_tensors_vector.size(); i++) {
if (i < 3 || (i > 4 && i < 9) || i > 10) {
if (GetDataType(amp_tensors_vector[i][0]) == DataType::FLOAT32) {
dst_type = DataType::FLOAT32;
return dst_type;
}
}
}
} else if (op_name == "fused_feedforward") {
for (size_t i = 0; i < amp_tensors_vector.size(); i++) {
if (i < 7 || i > 10) {
if (GetDataType(amp_tensors_vector[i][0]) == DataType::FLOAT32) {
dst_type = DataType::FLOAT32;
return dst_type;
}
}
}
}
}
for (const auto& tensors : amp_tensors_vector) {
for (const auto& tensor : tensors) {
if (GetDataType(tensor) == DataType::FLOAT32) {
dst_type = GetDataType(tensor);
break;
}
}
}
return dst_type;
}
static inline DataType GetDtypeWithPlace(
const std::string& op_name,
const paddle::small_vector<std::vector<paddle::Tensor>,
egr::kSlotSmallVectorSize>& amp_tensors_vector,
const DataType amp_dtype) {
if (amp_dtype == DataType::FLOAT32) {
return amp_dtype;
}
bool is_right_place = false;
for (const auto& tensors : amp_tensors_vector) {
for (const auto& tensor : tensors) {
auto place = tensor.place();
// TODO(lizhiyu): If the tensor is a dist-tensor, it's place may be
// `unknown` in the no-calculation rank right now.
// We use `is_dist_tensor()` to avoid the bug temporarily. The
// dist-tensor in the no-calculation rank should have the right
// place.
is_right_place =
(tensor.is_dist_tensor() || phi::is_gpu_place(place) ||
phi::is_cuda_pinned_place(place) || phi::is_xpu_place(place) ||
phi::is_custom_place(place));
if (is_right_place) {
break;
}
}
}
if (!is_right_place) {
VLOG(6) << "Change " << op_name << "'s AMP type from " << amp_dtype
<< " to FP32";
return DataType::FLOAT32;
}
return amp_dtype;
}
static inline DataType GetDtypeWithPlace(
const std::string& op_name UNUSED,
const paddle::small_vector<std::vector<pir::Value>,
egr::kSlotSmallVectorSize>& amp_tensors_vector
UNUSED,
const DataType amp_dtype) {
return amp_dtype;
}
template <class T>
inline DataType GetAmpDestDtype(
const std::string& op_name,
const paddle::small_vector<std::vector<T>, egr::kSlotSmallVectorSize>&
amp_tensors_vector) {
auto amp_level = egr::Controller::Instance().GetAMPLevel();
auto amp_setting_dtype =
egr::Controller::Instance().GetCurrentTracer()->GetAmpPhiDtype();
auto dst_type = amp_setting_dtype;
bool use_promote = true;
if (amp_level == paddle::imperative::AmpLevel::O2) {
use_promote =
egr::Controller::Instance().GetCurrentTracer()->GetUsePromote();
}
if (use_promote) {
if (paddle::imperative::AmpOperators::Instance()
.GetMutableAllowOps()
->count(op_name)) {
dst_type = amp_setting_dtype;
} else if (paddle::imperative::AmpOperators::Instance()
.GetMutableBlockOps()
->count(op_name)) {
dst_type = DataType::FLOAT32;
} else {
if (amp_level == paddle::imperative::AmpLevel::OD) {
dst_type = DataType::FLOAT32;
} else {
dst_type =
GetPromoteType(op_name, amp_tensors_vector, amp_setting_dtype);
}
}
} else {
// use_promote can be set to false only for O2 training.
if (paddle::imperative::AmpOperators::Instance()
.GetMutableBlockOps()
->count(op_name)) {
dst_type = DataType::FLOAT32;
}
}
if (dst_type == amp_setting_dtype &&
(paddle::imperative::AmpOperators::Instance()
.GetMutableUnsupportedOps(amp_setting_dtype)
->count(op_name))) {
dst_type = DataType::FLOAT32;
}
dst_type = GetDtypeWithPlace(op_name, amp_tensors_vector, dst_type);
VLOG(6) << "AMP GetAmpDestDtype:"
<< " op(" << op_name << ") amp_dtype(" << dst_type << ") amp_level("
<< static_cast<int>(amp_level) << ").";
return dst_type;
}
static inline bool NeedCast(const paddle::Tensor& tensor,
const DataType& dst_dtype) {
auto place = tensor.place();
auto data_type = tensor.dtype();
// Except CPU judgment, other conditions should be consistent with
// amp_utils.h's judgment
if (phi::is_gpu_place(place) || phi::is_cuda_pinned_place(place) ||
phi::is_xpu_place(place) || phi::is_custom_place(place) ||
phi::is_cpu_place(place)) {
// CudaPinnedPlace is added for varbase created by dataloader
// Cpu place is for different place tensor, when input1 is cpu and input2
// is gpu
if ((data_type == DataType::FLOAT32 || data_type == DataType::FLOAT16 ||
data_type == DataType::BFLOAT16) &&
(data_type != dst_dtype)) {
return true;
}
}
return false;
}
static inline bool NeedCast(const pir::Value& value,
const DataType& dst_dtype) {
auto data_type = paddle::dialect::GetValueDataType(value);
if ((data_type == DataType::FLOAT32 || data_type == DataType::FLOAT16 ||
data_type == DataType::BFLOAT16) &&
(data_type != dst_dtype)) {
return true;
}
return false;
}
#if !(defined(PADDLE_NO_PYTHON) && defined(PADDLE_ON_INFERENCE))
static inline paddle::Tensor Cast(const paddle::Tensor& input,
const DataType& dst_dtype,
const bool trace_backward = true) {
if (input.is_sparse_coo_tensor() || input.is_sparse_csr_tensor()) {
if (trace_backward) {
return sparse::cast_ad_func(input, DataType::UNDEFINED, dst_dtype);
} else {
return paddle::experimental::sparse::cast(
input, DataType::UNDEFINED, dst_dtype);
}
} else {
if (trace_backward) {
return cast_ad_func(input, dst_dtype);
} else {
return paddle::experimental::cast(input, dst_dtype);
}
}
}
#endif
static inline pir::Value Cast(const pir::Value& input,
const DataType& dst_dtype,
const bool trace_backward UNUSED = true) {
paddle::imperative::AutoCastGuard guard(
egr::Controller::Instance().GetCurrentAmpAttrs(),
paddle::imperative::AmpLevel::O0);
return paddle::dialect::cast(input, dst_dtype);
}
template <class T>
inline std::vector<T> AmpAutoCasts(const std::string& inputs_name,
const std::vector<T>& inputs,
const DataType& dst_dtype,
std::string op_name UNUSED,
bool trace_backward UNUSED = true) {
VLOG(6) << "AMP AmpAutoCasts:"
<< " inputs(" << inputs_name << ") dst_dtype("
<< DataTypeToString(dst_dtype) << ").";
std::vector<T> inputs_casted;
for (auto& input : inputs) {
if (NeedCast(input, dst_dtype)) {
inputs_casted.emplace_back(std::move(Cast(input, dst_dtype)));
} else {
inputs_casted.emplace_back(input);
}
}
return inputs_casted;
}
template <class T>
inline T AmpAutoCast(const std::string& input_name,
const T& input,
const DataType& dst_dtype,
const std::string& op_name,
bool trace_backward = true) {
VLOG(6) << "AMP AmpAutoCasts: op_name(" << op_name << ")input(" << input_name
<< ") dst_dtype(" << DataTypeToString(dst_dtype) << ").";
if (dst_dtype == DataType::FLOAT16) {
if (op_name == "run_program") {
return input;
}
if ((op_name == "fused_attention" || op_name == "fused_feedforward")) {
if (input_name == "LnScale" || input_name == "LnBias" ||
input_name == "Ln2Scale" || input_name == "Ln2Bias" ||
input_name == "Ln1Scale" || input_name == "Ln1Bias") {
return input;
}
if (input_name == "ln_scale" || input_name == "ln_bias" ||
input_name == "ln_scale_2" || input_name == "ln_bias_2" ||
input_name == "ln1_scale" || input_name == "ln1_bias" ||
input_name == "ln2_scale" || input_name == "ln2_bias") {
return input;
}
}
if ((op_name == "batch_norm" || op_name == "layer_norm" ||
op_name == "sync_batch_norm" || op_name == "weight_only_linear") &&
input_name != "x") {
return input;
}
} else if (dst_dtype == DataType::BFLOAT16) {
if ((op_name == "batch_norm" || op_name == "layer_norm" ||
op_name == "sync_batch_norm" || op_name == "weight_only_linear") &&
input_name != "x") {
return input;
}
}
if (NeedCast(input, dst_dtype)) {
VLOG(6) << "Input : " << input.impl() << "NeedCast";
return Cast(input, dst_dtype, trace_backward);
}
return input;
}
template <class T>
inline paddle::optional<T> AmpAutoCast(const std::string& input_name,
const paddle::optional<T>& input,
const DataType& dst_dtype,
const std::string& op_name,
bool trace_backward = true) {
if (input) {
return AmpAutoCast(input_name, *input, dst_dtype, op_name, trace_backward);
}
return paddle::none;
}
template <class T>
inline paddle::optional<std::vector<T>> AmpAutoCasts(
const std::string& inputs_name,
const paddle::optional<std::vector<T>>& inputs,
const DataType& dst_dtype,
std::string op_name,
bool trace_backward = true) {
if (inputs) {
return AmpAutoCasts(
inputs_name, *inputs, dst_dtype, op_name, trace_backward);
}
return paddle::optional<std::vector<T>>();
}
} // namespace imperative
} // namespace paddle