Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support inplace in dygraph eager_fluid state #40400

Merged
merged 61 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
9fc70fe
[Eager] Support eager grad interface, draft version
veyron95 Mar 4, 2022
ba8d79e
Support eager grad interface with allow_unused and multi startup_op
veyron95 Mar 7, 2022
137db9d
Fix code format
veyron95 Mar 8, 2022
1a18aa2
Fix allow_unused case, return PyNone if tensor not initialize
veyron95 Mar 8, 2022
d09ec3b
Support output's stop_gradient related to create_graph
veyron95 Mar 8, 2022
f84f2be
Support grad exception case in eager mode, fix coverage CI
veyron95 Mar 8, 2022
733672e
Update ToPyObject, return PyNone if not initialize
veyron95 Mar 8, 2022
68b1991
AccumulationNode add FLAGS_retain_grad_for_all_tensor
veyron95 Mar 8, 2022
7665d63
Fix ci issue
veyron95 Mar 9, 2022
86393f5
Fix CI issue
veyron95 Mar 9, 2022
c653ec0
fix, use core.eager.Tensor
veyron95 Mar 9, 2022
9156cea
Add func SetBufferSlotRankZeros for GradTensorHolder
veyron95 Mar 9, 2022
6fd613d
Support retain_graph by using ClearTensorWrappers
veyron95 Mar 9, 2022
58731e9
Support retain_graph by using ClearTensorWrappers
veyron95 Mar 9, 2022
a88f9b1
Update retain_graph and no_grad_vars related test case
veyron95 Mar 9, 2022
778719b
Update code gen logic for ClearTensorWrappers
veyron95 Mar 10, 2022
65cf9e3
Fix by override statement
veyron95 Mar 10, 2022
af7b919
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 10, 2022
4d3b57d
fix override func args
veyron95 Mar 10, 2022
415ff65
Support retain_graph, update unit tests
veyron95 Mar 10, 2022
bb283ce
Updated ClearTensorWrappers logic
veyron95 Mar 10, 2022
e548c22
fix grad python interface
veyron95 Mar 11, 2022
519c9a6
Use deep copy and update unit tests
veyron95 Mar 11, 2022
1fbc61b
Polish code
veyron95 Mar 11, 2022
c0a2b8b
Polish code
veyron95 Mar 11, 2022
536a28b
Fix CI issue, Deep copy only use when user set grad_tensors
veyron95 Mar 11, 2022
2417858
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 11, 2022
34fa7c0
Fix CI, use Backward instead RunBackward
veyron95 Mar 11, 2022
1b89072
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 12, 2022
af7f058
Fix CI, Declare kernel explicitly in test file
veyron95 Mar 12, 2022
f397b8f
Polish, remove vector of TensorWrapper
veyron95 Mar 14, 2022
e3f9826
Refactor the logic of grad/backward, polish codes
veyron95 Mar 14, 2022
27830a9
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 14, 2022
7ede919
Update code after merge upstream develop
veyron95 Mar 14, 2022
f9adf49
Polish after merge upstream develop
veyron95 Mar 14, 2022
2fe3b9f
Update to adapt new GradNodeBase superclass
veyron95 Mar 14, 2022
90e97d6
Fix error introduced during conflict resolution
veyron95 Mar 14, 2022
8c27961
support inplace strategy in eager_fluid state
pangyoki Mar 15, 2022
daff8bd
solve conflict
pangyoki Mar 15, 2022
db573fe
solve conflict
pangyoki Mar 15, 2022
f0d8f65
nothing
pangyoki Mar 15, 2022
d18697a
Update purify potential_startup_nodes logic
veyron95 Mar 15, 2022
1b5eac2
Fix errors
veyron95 Mar 15, 2022
f4e42e2
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 15, 2022
58a03b5
Polish code
veyron95 Mar 15, 2022
ea41a1c
solve conflict
pangyoki Mar 15, 2022
b04e9a9
Remove useless args for ToPyObject
veyron95 Mar 15, 2022
c7bd6fc
Remove useless TensorWrappersSet
veyron95 Mar 15, 2022
7bb3cbd
fix record conflict
pangyoki Mar 15, 2022
ac85d81
Fix code-format, re-install pre-commit
veyron95 Mar 16, 2022
945d282
fix tensor_wrapper bug
pangyoki Mar 16, 2022
8312d2d
Fix pre-process logic for potential_startup_ops
veyron95 Mar 16, 2022
441bc81
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 16, 2022
326eee5
Update unit tests, use eager mode
veyron95 Mar 16, 2022
3562b64
solve conflict
pangyoki Mar 17, 2022
a0ec433
Merge branch 'develop' of https://github.com/PaddlePaddle/Paddle into…
veyron95 Mar 17, 2022
df99eea
Fix conflicts
veyron95 Mar 17, 2022
cecc6e1
Merge commit 'refs/pull/40655/head' of https://github.com/PaddlePaddl…
pangyoki Mar 17, 2022
9491a06
fix unittest timeout
pangyoki Mar 18, 2022
894791c
solve conflict
pangyoki Mar 18, 2022
41dec57
little change
pangyoki Mar 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion paddle/fluid/eager/api/utils/tensor_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ namespace egr_utils_api {

bool IsLeafTensor(const paddle::experimental::Tensor& target) {
std::shared_ptr<GradNodeBase> grad_node = EagerUtils::grad_node(target);
if (std::dynamic_pointer_cast<GradNodeAccumulation>(grad_node)) {
if (!grad_node ||
std::dynamic_pointer_cast<GradNodeAccumulation>(grad_node)) {
return true;
}

Expand Down
396 changes: 290 additions & 106 deletions paddle/fluid/eager/auto_code_generator/eager_generator.cc

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions paddle/fluid/eager/tensor_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ class TensorWrapper {
explicit TensorWrapper(const paddle::experimental::Tensor& tensor,
bool full_reserved = false,
bool no_need_buffer = false) {
// set inplace_version_snapshot_ according to tensor's current inplace
// version.
if (tensor.impl() && phi::DenseTensor::classof(tensor.impl().get())) {
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();
}

/**
* Normally, we should fully reserved all non-output or non-leaf fwd tensor
* here. And for fwd output tensor, we should not reserve its autogradmeta,
Expand All @@ -49,6 +58,7 @@ class TensorWrapper {
}

// shallow copy tensor_impl here
no_need_buffer_ = no_need_buffer;
if (no_need_buffer) {
if (phi::DenseTensor::classof(tensor.impl().get())) {
// Only Copy Meta
Expand Down Expand Up @@ -86,6 +96,7 @@ class TensorWrapper {

// if it's full_reserved just return the full copy of tensor
if (full_reserved_) {
check_inplace_version();
return intermidiate_tensor_;
} else {
std::shared_ptr<GradNodeBase> new_grad_node = grad_node;
Expand All @@ -94,15 +105,52 @@ class TensorWrapper {
intermidiate_tensor_.set_autograd_meta(
std::static_pointer_cast<paddle::experimental::AbstractAutogradMeta>(
p_ab_autograd_meta));
check_inplace_version();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're gonna check inplace version anyway, let's move this function "check_inplace_version" out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #41118

return intermidiate_tensor_;
}
}

void check_inplace_version() {
if (no_need_buffer_) {
VLOG(6) << "There's no need to check inplace_version because "
"no_need_buffer_ is true.";
return;
}
if (intermidiate_tensor_.impl() &&
phi::DenseTensor::classof(intermidiate_tensor_.impl().get())) {
phi::DenseTensor* dense_tensor =
static_cast<phi::DenseTensor*>(intermidiate_tensor_.impl().get());
auto& inplace_version_counter = dense_tensor->InplaceVersionCounter();

uint32_t current_inplace_version =
inplace_version_counter.CurrentVersion();
PADDLE_ENFORCE_EQ(
current_inplace_version, inplace_version_snapshot_,
paddle::platform::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.",
intermidiate_tensor_.name(), current_inplace_version,
inplace_version_snapshot_));
VLOG(6) << " The inplace_version_snapshot_ of Tensor '"
<< intermidiate_tensor_.name() << "' is [ "
<< inplace_version_snapshot_ << " ]";
VLOG(6) << " The current_inplace_version of Tensor '"
<< intermidiate_tensor_.name() << "' is [ "
<< current_inplace_version << " ]";
}
}

void clear() { intermidiate_tensor_.reset(); }

private:
bool full_reserved_ = false;
bool no_need_buffer_ = false;
std::pair<size_t, size_t> out_rank_info_;
paddle::experimental::Tensor intermidiate_tensor_;
uint32_t inplace_version_snapshot_ = 0;
};
} // namespace egr
21 changes: 21 additions & 0 deletions paddle/fluid/eager/utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ std::vector<std::shared_ptr<EagerVariable>> EagerUtils::CreateVars(
return res;
}

void EagerUtils::ModifyInplaceInput(
const std::shared_ptr<EagerVariable>& inplace_variable,
paddle::experimental::Tensor* inplace_tensor) {
// Only modify the meta information of the inplace tensor, because
// EagerVariable cannot modify Tensor's meta information after inplace
// op (such as ``reshape``) is executed.
PADDLE_ENFORCE_NOT_NULL(inplace_tensor,
paddle::platform::errors::Fatal(
"Inplace Tensor is null and cannot be modified. "
"We are tring to Modify Inplace Input from its "
"shared_ptr, this error may indicate the inplace "
" input is nullptr"));
if (phi::DenseTensor::classof(inplace_variable->GetTensorBase().get())) {
phi::DenseTensor* variable_dense_tensor =
static_cast<phi::DenseTensor*>(inplace_variable->GetTensorBase().get());
phi::DenseTensor* tensor_dense_tensor =
static_cast<phi::DenseTensor*>(inplace_tensor->impl().get());
tensor_dense_tensor->set_meta(variable_dense_tensor->meta());
}
}

std::vector<paddle::experimental::Tensor> EagerUtils::GetOutputs(
const std::vector<std::shared_ptr<EagerVariable>>& outs) {
std::vector<paddle::experimental::Tensor> res;
Expand Down
17 changes: 17 additions & 0 deletions paddle/fluid/eager/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#pragma once

#include "paddle/fluid/eager/api/utils/tensor_utils.h"
#include "paddle/fluid/eager/autograd_meta.h"
#include "paddle/fluid/eager/eager_tensor.h"
#include "paddle/fluid/eager/grad_node_info.h"
Expand Down Expand Up @@ -144,6 +145,19 @@ class EagerUtils {
iter.apply(std::forward<Args>(args)...);
}

static void CheckInplace(const paddle::experimental::Tensor& target,
const AutogradMeta* autograd_meta,
bool require_any_grad) {
if (require_any_grad && autograd_meta) {
PADDLE_ENFORCE_EQ(!autograd_meta->StopGradient() &&
egr::egr_utils_api::IsLeafTensor(target),
false, paddle::platform::errors::InvalidArgument(
"Leaf Var (%s) that doesn't stop gradient "
"can't use inplace strategy.",
target.name()));
}
}

// TensorWrapper Utils
static paddle::experimental::Tensor RecoverTensorWrapper(
TensorWrapper* tw, const std::shared_ptr<GradNodeBase>& grad_node);
Expand Down Expand Up @@ -171,6 +185,9 @@ class EagerUtils {
static std::vector<std::shared_ptr<EagerVariable>> CreateVars(
const size_t num);
// Construct Tensor From var
static void ModifyInplaceInput(
const std::shared_ptr<EagerVariable>& inplace_variable,
paddle::experimental::Tensor* inplace_tensor);
static std::vector<paddle::experimental::Tensor> GetOutputs(
const std::vector<std::shared_ptr<EagerVariable>>& outs);
static paddle::experimental::Tensor GetOutput(
Expand Down
11 changes: 11 additions & 0 deletions paddle/fluid/pybind/eager_method.cc
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,15 @@ static PyObject* set_grad_type(TensorObject* self, PyObject* args,
EAGER_CATCH_AND_THROW_RETURN_NULL
}

static PyObject* tensor__inplace_version(TensorObject* self, PyObject* args,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

single underscore "_" in function name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its ok if this method indicate _inplace_version api in python

PyObject* kwargs) {
EAGER_TRY
uint32_t inplace_version = self->tensor.current_inplace_version();

return ToPyObject(inplace_version);
EAGER_CATCH_AND_THROW_RETURN_NULL
}

PyMethodDef variable_methods[] = {
{"numpy", (PyCFunction)(void (*)(void))tensor_method_numpy,
METH_VARARGS | METH_KEYWORDS, NULL},
Expand Down Expand Up @@ -766,6 +775,8 @@ PyMethodDef variable_methods[] = {
METH_VARARGS | METH_KEYWORDS, NULL},
{"_set_grad_type", (PyCFunction)(void (*)(void))set_grad_type,
METH_VARARGS | METH_KEYWORDS, NULL},
{"_inplace_version", (PyCFunction)(void (*)(void))tensor__inplace_version,
METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL, 0, NULL}};

} // namespace pybind
Expand Down
73 changes: 68 additions & 5 deletions paddle/fluid/pybind/eager_op_function_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,22 @@ static inline std::string TempName(const std::string& name) {

std::string GenerateOpFunctionsBody(
const paddle::framework::proto::OpProto* op_proto, std::string func_name,
bool use_inplace_strategy = false,
std::map<std::string, std::string> inplace_map = {}) {
auto& op_type = op_proto->type();
std::string input_args = "";
std::string call_api_str = "auto out = " + op_type + "_dygraph_function(";
std::string call_api_str = "";
std::string ins_initializer_with_null = "";
std::string py_arg = "";
int arg_idx = 0;
int input_args_num = 0;
std::string ins_cast_str = "";
std::string view_strategy_str = "";
if (!inplace_map.empty()) {
// change call_api_str for inplace op
call_api_str = "auto out = " + op_type + "__dygraph_function(";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better add "" at the very end of the function name, like "scale_dygraph_function" for inplaced scale

} else {
call_api_str = "auto out = " + op_type + "_dygraph_function(";
}
for (auto& input : op_proto->inputs()) {
auto& in_name = input.name();
// skip those dispensable inputs, like ResidualData in conv2d
Expand Down Expand Up @@ -288,8 +293,31 @@ std::string GenerateOpFunctionsBody(
HANDLE_VIEW_BETWEEN_INPUT_AND_OUTPUT, viwe_input_name, viwe_output_name,
viwe_input_name, viwe_output_name);
}

return_str = "return ToPyObject(out);";
if (!inplace_map.empty()) {
// For inplace op, Use the input PyObject directly.
for (auto& inplace_pair : inplace_map) {
// Find index of inplace tensor, and directly use input PyObject.
std::string inplace_arg_name = inplace_pair.second;
std::string inplace_return_name = inplace_pair.first;
const char* RETURN_INPLACE_TENSOR_TEMPLATE =
"ssize_t arg_id = GetIdxFromCoreOpsInfoMap(core_ops_args_info, "
"\"%s\", \"%s\");\n"
" ssize_t return_id = "
"GetIdxFromCoreOpsInfoMap(core_ops_returns_info, \"%s\", \"%s\");\n"
" return ToPyObject(out, return_id, args, arg_id);";
return_str = paddle::string::Sprintf(RETURN_INPLACE_TENSOR_TEMPLATE,
op_type, inplace_arg_name, op_type,
inplace_return_name);
// only support one inplace_var in temporary.
PADDLE_ENFORCE_EQ(
inplace_map.size(), 1,
paddle::platform::errors::InvalidArgument(
"size of inplace_map must be 1, but got %d", inplace_map.size()));
break;
}
} else {
return_str = "return ToPyObject(out);";
}

std::string function_args = "";
if (input_args == "") {
Expand Down Expand Up @@ -383,14 +411,49 @@ GenerateOpFunctions() {
continue;
}
std::string func_name = "eager_api_" + op_type;
std::string op_function_str = GenerateOpFunctionsBody(op_proto, func_name);
std::string op_function_str =
GenerateOpFunctionsBody(op_proto, func_name, {});

// generate pybind item
auto bind_function_str = paddle::string::Sprintf(
PYBIND_ITEM_TEMPLATE, op_type, func_name, op_type);

op_function_list.emplace_back(std::move(op_function_str));
bind_function_list.emplace_back(std::move(bind_function_str));

// NOTE(pangyoki): Inplace Strategy.
// In this case, output will reuse input varbase.
// Dygraph mode needs to be aligned with the in-place strategy in static
// mode, and the mapping relationships between output and input that have
// been defined in static mode should be used in dygraph mode.
// Find which ops need to use Inplace strategy in static mode, and get the
// mapping relationship between Inplace output and input.
auto& infer_inplace =
paddle::framework::OpInfoMap::Instance().Get(op_type).infer_inplace_;
std::map<std::string, std::string> inplace_map;
// `sum` op has duplicate input. Don't consider adding inplace strategy
// for `sum` in temporary.
if (op_type != "sum" && infer_inplace) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better store hard-coded op name in a static set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in PR #41118

// Inplace OP: op_type_.
// The inplace OP needs a new implementation method.
auto in_to_outs = infer_inplace(true);
for (auto& inplace_pair : in_to_outs) {
inplace_map[inplace_pair.second] = inplace_pair.first;
}

std::string inplace_op_type = op_type + "_";
std::string inplace_func_name = "eager_api_" + inplace_op_type;
std::string inplace_op_function_str =
GenerateOpFunctionsBody(op_proto, inplace_func_name, inplace_map);

// generate pybind item
auto inplace_bind_function_str =
paddle::string::Sprintf(PYBIND_ITEM_TEMPLATE, inplace_op_type,
inplace_func_name, inplace_op_type);

op_function_list.emplace_back(std::move(inplace_op_function_str));
bind_function_list.emplace_back(std::move(inplace_bind_function_str));
}
}
if (append_custom_head_file) {
op_function_list.emplace_back(CUSTOM_HANDWRITE_OP_FUNC_FILE);
Expand Down
16 changes: 16 additions & 0 deletions paddle/fluid/pybind/eager_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ PyObject* ToPyObject(bool value) {

PyObject* ToPyObject(int value) { return PyLong_FromLong(value); }

PyObject* ToPyObject(uint32_t value) { return PyLong_FromUnsignedLong(value); }

PyObject* ToPyObject(int64_t value) { return PyLong_FromLongLong(value); }

PyObject* ToPyObject(float value) { return PyLong_FromDouble(value); }
Expand All @@ -442,6 +444,20 @@ PyObject* ToPyObject(const paddle::experimental::Tensor& value) {
return obj;
}

PyObject* ToPyObject(const paddle::experimental::Tensor& value,
ssize_t value_idx, PyObject* args, ssize_t arg_idx) {
// For inplace op, directly return the input PyObject of the inplace tensor.
// [Parameter]
// value: Useless parameter.
// value_idx: Useless parameter.
// args: Input PyObject.
// arg_idx: Index of inplace PyObject in input args. Used to find the input
// inplace PyObject.
PyObject* obj = PyTuple_GET_ITEM(args, arg_idx);
Py_INCREF(obj);
return obj;
}

PyObject* ToPyObject(const std::vector<bool>& value) {
PyObject* result = PyList_New((Py_ssize_t)value.size());

Expand Down
Loading