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

307 lines
12 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.
/**
* We now still need TensorWrapper and it is designed to Copy
* tensor in autograd mode.
*
* Since in autograd usage, we need to pass autograd_meta to
* backward computation however in tensor interface add to much
* autograd_related method is not a good choice.
*
* In TensorWrapper we will keep autograd info to backward, only
* for input var, but for output var it will only copy autograd
* with no grad **/
#pragma once
#include "paddle/fluid/eager/autograd_meta.h"
#include "paddle/fluid/eager/grad_node_info.h"
#include "paddle/fluid/eager/utils.h"
#include "paddle/phi/api/lib/utils/allocator.h"
#ifndef PADDLE_NO_PYTHON
#include "paddle/fluid/eager/hooks.h"
#endif
#ifdef PADDLE_WITH_CUDA
#include "paddle/fluid/eager/activation_offloader.h"
#endif
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
COMMON_DECLARE_int64(offload_retry_times);
namespace egr {
class TensorWrapper {
public:
TensorWrapper() = default;
explicit TensorWrapper(const paddle::Tensor& tensor,
bool no_need_buffer = false) {
// set inplace_version_snapshot_ according to tensor's current inplace
// version.
if (tensor.has_allocation() && tensor.is_dense_tensor()) {
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(tensor.impl().get());
auto& inplace_version_counter = dense_tensor->InplaceVersionCounter();
inplace_version_snapshot_ = inplace_version_counter.CurrentVersion();
} else if (tensor.has_allocation() && tensor.is_dist_tensor()) {
phi::DenseTensor* dense_tensor =
static_cast<phi::distributed::DistTensor*>(tensor.impl().get())
->unsafe_mutable_value();
auto& inplace_version_counter = dense_tensor->InplaceVersionCounter();
inplace_version_snapshot_ = inplace_version_counter.CurrentVersion();
}
/**
* Normally, we should only save data and part of autograd_meta of fwd
* tensor, and should not reserve its original grad_node,
* to avoid recursive and additional depends on GradNodeBase
* **/
auto* tensor_autograd_meta = EagerUtils::nullable_autograd_meta(tensor);
no_need_buffer_ = no_need_buffer;
// shallow copy tensor_impl here
if (no_need_buffer) {
if (phi::DenseTensor::classof(tensor.impl().get())) {
// Only Copy Meta
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(tensor.impl().get());
// TODO(jiabin): It's not a good idea to set memory size to zero, find
// another way and change this.
intermediate_tensor_.set_impl(std::make_shared<phi::DenseTensor>(
std::make_shared<phi::Allocation>(nullptr, 0, tensor.place()),
dense_tensor->meta()));
} else if (phi::distributed::DistTensor::classof(tensor.impl().get())) {
// Copy Global dims, DistAttr and DenseTensorMeta
phi::distributed::DistTensor* dist_tensor =
static_cast<phi::distributed::DistTensor*>(tensor.impl().get());
auto no_buffer_dist_tensor =
std::make_shared<phi::distributed::DistTensor>(
dist_tensor->dims(), dist_tensor->dist_attr());
*no_buffer_dist_tensor->unsafe_mutable_value() = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, tensor.place()),
dist_tensor->value().meta());
intermediate_tensor_.set_impl(no_buffer_dist_tensor);
} else {
PADDLE_THROW(common::errors::Fatal(
"Unrecognized tensor type for no_need_buffer feature"));
}
} else {
#ifndef PADDLE_NO_PYTHON
if (egr::SavedTensorsHooks::GetInstance().IsEnable() &&
tensor.has_allocation() && tensor.is_dense_tensor()) {
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(tensor.impl().get());
intermediate_tensor_.set_impl(std::make_shared<phi::DenseTensor>(
std::make_shared<phi::Allocation>(nullptr, 0, tensor.place()),
dense_tensor->meta()));
auto pack_hook = egr::SavedTensorsHooks::GetInstance().GetPackHook();
unpack_hook_ = egr::SavedTensorsHooks::GetInstance().GetUnPackHook();
packed_value_ = (*pack_hook)(tensor);
} else if (egr::SavedTensorsHooks::GetInstance().IsEnable() &&
tensor.has_allocation() && tensor.is_dist_tensor()) {
intermediate_tensor_.set_impl(
std::make_shared<phi::distributed::DistTensor>(
tensor.dims(),
static_cast<phi::distributed::DistTensor*>(tensor.impl().get())
->dist_attr()));
auto dense_tensor =
static_cast<phi::distributed::DistTensor*>(tensor.impl().get())
->value();
phi::DenseTensor tmp(
std::make_shared<phi::Allocation>(nullptr, 0, tensor.place()),
dense_tensor.meta());
*(static_cast<phi::distributed::DistTensor*>(
intermediate_tensor_.impl().get())
->unsafe_mutable_value()) = tmp;
auto pack_hook = egr::SavedTensorsHooks::GetInstance().GetPackHook();
unpack_hook_ = egr::SavedTensorsHooks::GetInstance().GetUnPackHook();
packed_value_ = (*pack_hook)(tensor);
} else {
#endif
intermediate_tensor_.set_impl(tensor.impl());
#ifndef PADDLE_NO_PYTHON
}
#endif
}
if (VLOG_IS_ON(6)) {
// We should copy the name for debug.
intermediate_tensor_.set_name(tensor.name());
}
if (VLOG_IS_ON(11)) {
// TODO(jiabin): This may has server performance issue
intermediate_tensor_.set_name(tensor.name() + "@Saved");
}
if (tensor_autograd_meta) {
auto autograd_meta =
std::make_shared<AutogradMeta>(*tensor_autograd_meta);
autograd_meta->ResetGradNode();
intermediate_tensor_.set_autograd_meta(autograd_meta);
weak_grad_node_ = tensor_autograd_meta->GetMutableGradNode();
}
#ifdef PADDLE_WITH_CUDA
if (FLAGS_offload_retry_times > 0) {
reload_functor_ =
ActivationOffloader::Instance()->Add(intermediate_tensor_);
}
#endif
}
paddle::Tensor recover() {
VLOG(6) << "Recover tensor: " << intermediate_tensor_.name()
<< " for wrapper";
#ifdef PADDLE_WITH_CUDA
if (auto reload_functor_ptr = reload_functor_.get_ptr()) {
reload_functor_ptr->Reload();
}
#endif
if (!intermediate_tensor_.defined()) {
VLOG(6) << "Return NULL tensor Here. ";
return paddle::Tensor();
}
#ifndef PADDLE_NO_PYTHON
if (packed_value_ && unpack_hook_) {
auto tensor_unpacked = (*unpack_hook_)(packed_value_);
phi::DenseTensor* src_dense_tensor = nullptr;
if (tensor_unpacked.is_dense_tensor()) {
VLOG(6) << "tensor_unpacked is DenseTensor";
src_dense_tensor =
static_cast<phi::DenseTensor*>(tensor_unpacked.impl().get());
} else if (tensor_unpacked.is_dist_tensor()) {
VLOG(6) << "tensor_unpacked is DistTensor";
src_dense_tensor = static_cast<phi::distributed::DistTensor*>(
tensor_unpacked.impl().get())
->unsafe_mutable_value();
} else {
PADDLE_THROW(
common::errors::Fatal("Unrecognized tensor_unpacked type "
"for egr::TensorWrapper::recover"));
}
if (intermediate_tensor_.is_dense_tensor()) {
VLOG(6) << "intermediate_tensor_ is DenseTensor";
static_cast<phi::DenseTensor*>(intermediate_tensor_.impl().get())
->ResetHolder(src_dense_tensor->Holder());
} else if (intermediate_tensor_.is_dist_tensor()) {
VLOG(6) << "intermediate_tensor_ is DistTensor";
static_cast<phi::distributed::DistTensor*>(
intermediate_tensor_.impl().get())
->unsafe_mutable_value()
->ResetHolder(src_dense_tensor->Holder());
} else {
PADDLE_THROW(
common::errors::Fatal("Unrecognized intermediate_tensor_ type for "
"egr::TensorWrapper::recover"));
}
} else {
#endif
check_inplace_version();
#ifndef PADDLE_NO_PYTHON
}
#endif
paddle::Tensor recovered_tensor = intermediate_tensor_;
std::shared_ptr<GradNodeBase> new_grad_node = weak_grad_node_.lock();
if (new_grad_node) {
VLOG(7) << "Recovered TensorWrapper with GradNode "
<< new_grad_node->name() << " addr: " << new_grad_node.get();
} else {
VLOG(7) << "Recovered TensorWrapper with Empty GradNode";
}
auto* intermediate_autograd_meta =
EagerUtils::nullable_autograd_meta(intermediate_tensor_);
if (intermediate_autograd_meta) {
auto p_ab_autograd_meta =
std::make_shared<AutogradMeta>(*intermediate_autograd_meta);
if (new_grad_node) {
p_ab_autograd_meta->SetGradNode(new_grad_node);
}
recovered_tensor.set_autograd_meta(p_ab_autograd_meta);
}
return recovered_tensor;
}
paddle::Tensor get_intermediate_tensor() { return intermediate_tensor_; }
void clear() { intermediate_tensor_.reset(); }
private:
void check_inplace_version() {
if (no_need_buffer_) {
VLOG(7) << "There's no need to check inplace_version because "
"no_need_buffer_ is true.";
return;
}
if (intermediate_tensor_.impl()) {
phi::DenseTensor* dense_tensor = nullptr;
if (phi::DenseTensor::classof(intermediate_tensor_.impl().get())) {
dense_tensor =
static_cast<phi::DenseTensor*>(intermediate_tensor_.impl().get());
} else if (phi::distributed::DistTensor::classof(
intermediate_tensor_.impl().get())) {
dense_tensor = static_cast<phi::distributed::DistTensor*>(
intermediate_tensor_.impl().get())
->unsafe_mutable_value();
} else {
return;
}
auto& inplace_version_counter = dense_tensor->InplaceVersionCounter();
uint32_t wrapper_version_snapshot = inplace_version_snapshot_;
uint32_t tensor_version = inplace_version_counter.CurrentVersion();
PADDLE_ENFORCE_EQ(
tensor_version,
wrapper_version_snapshot,
common::errors::PermissionDenied(
"Tensor '%s' used in gradient computation has been "
"modified by an inplace operation. "
"Its version is %d but the expected version is %d. "
"Please fix your code to void calling an inplace operator "
"after using the Tensor which will used in gradient "
"computation.",
intermediate_tensor_.name(),
tensor_version,
wrapper_version_snapshot));
VLOG(7) << " The wrapper_version_snapshot of Tensor '"
<< intermediate_tensor_.name() << "' is [ "
<< wrapper_version_snapshot << " ]";
VLOG(7) << " The tensor_version of Tensor '"
<< intermediate_tensor_.name() << "' is [ " << tensor_version
<< " ]";
}
}
private:
bool no_need_buffer_ = false;
paddle::Tensor intermediate_tensor_;
#ifdef PADDLE_WITH_CUDA
paddle::optional<egr::ReloadFunctor> reload_functor_;
#endif
std::weak_ptr<egr::GradNodeBase> weak_grad_node_;
uint32_t inplace_version_snapshot_ = 0;
#ifndef PADDLE_NO_PYTHON
std::shared_ptr<egr::PyObjectHolderBase> packed_value_;
std::shared_ptr<egr::UnPackHookBase> unpack_hook_;
#else
std::shared_ptr<void> packed_value_;
std::shared_ptr<void> unpack_hook_;
#endif
};
} // namespace egr