1763 lines
70 KiB
C++
1763 lines
70 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. */
|
|
// disable numpy compile error
|
|
|
|
#if defined(_MSC_VER)
|
|
#include <BaseTsd.h>
|
|
typedef SSIZE_T ssize_t;
|
|
#endif
|
|
|
|
#include <Python.h>
|
|
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "paddle/fluid/eager/accumulation/accumulation_node.h"
|
|
#include "paddle/fluid/eager/api/all.h"
|
|
#include "paddle/fluid/eager/autograd_meta.h"
|
|
#include "paddle/fluid/eager/backward.h"
|
|
#include "paddle/fluid/eager/custom_operator/custom_operator_node.h"
|
|
#include "paddle/fluid/eager/utils.h"
|
|
#include "paddle/fluid/framework/convert_utils.h"
|
|
#include "paddle/fluid/framework/custom_operator.h"
|
|
#include "paddle/fluid/framework/custom_operator_utils.h"
|
|
#include "paddle/fluid/framework/phi_utils.h"
|
|
#include "paddle/fluid/framework/python_headers.h"
|
|
#include "paddle/fluid/platform/enforce.h"
|
|
#include "paddle/fluid/pybind/eager.h"
|
|
#include "paddle/fluid/pybind/eager_utils.h"
|
|
#include "paddle/fluid/pybind/exception.h"
|
|
#include "paddle/fluid/pybind/op_function_common.h"
|
|
#include "paddle/fluid/pybind/tensor_py.h"
|
|
#include "paddle/phi/api/ext/op_meta_info.h"
|
|
#include "paddle/phi/api/include/api.h"
|
|
#include "paddle/phi/api/lib/utils/allocator.h"
|
|
#include "paddle/phi/backends/dynload/dynamic_loader.h"
|
|
#include "paddle/phi/common/data_type.h"
|
|
#include "paddle/phi/core/compat/convert_utils.h"
|
|
#include "paddle/phi/core/dense_tensor.h"
|
|
#include "paddle/phi/core/memory/allocation/allocator.h"
|
|
#include "paddle/phi/core/memory/memcpy.h"
|
|
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
|
|
#include "paddle/phi/core/sparse_coo_tensor.h"
|
|
#include "paddle/phi/core/sparse_csr_tensor.h"
|
|
#include "paddle/utils/string/string_helper.h"
|
|
#include "pybind11/numpy.h"
|
|
#include "pybind11/pybind11.h"
|
|
|
|
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
|
#include "paddle/fluid/pybind/cuda_streams_py.h"
|
|
#endif
|
|
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
#include "paddle/phi/backends/gpu/cuda/cuda_graph.h"
|
|
#include "paddle/phi/kernels/legacy/gpu/tensor_debug.h"
|
|
#endif
|
|
|
|
#include "paddle/common/flags.h"
|
|
#include "paddle/fluid/eager/custom_operator/custom_operator_utils.h"
|
|
#include "paddle/phi/api/include/operants_manager.h"
|
|
#include "paddle/phi/api/include/tensor_operants.h"
|
|
#include "paddle/phi/api/lib/data_transform.h"
|
|
#ifdef PADDLE_WITH_DISTRIBUTE
|
|
#include "paddle/phi/api/lib/api_gen_utils.h"
|
|
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
|
#include "paddle/phi/infermeta/spmd_rules/rules.h"
|
|
#endif
|
|
|
|
COMMON_DECLARE_string(tensor_operants_mode);
|
|
COMMON_DECLARE_bool(check_cuda_error);
|
|
COMMON_DECLARE_bool(enable_unique_name);
|
|
COMMON_DECLARE_string(tensor_md5_checksum_output_path);
|
|
COMMON_DECLARE_bool(enable_compact_mem);
|
|
|
|
using egr::ConvertAllInputsToDistTensor;
|
|
using egr::InputsContainDistTensor;
|
|
|
|
namespace paddle::pybind {
|
|
|
|
namespace py = ::pybind11;
|
|
|
|
extern PyTypeObject* p_tensor_type;
|
|
extern PyTypeObject* g_multidevicefeedreader_pytype;
|
|
extern PyTypeObject* g_orderedmultidevicefeedreader_pytype;
|
|
|
|
size_t PyArray_Size_(PyObject* numpy_data) {
|
|
size_t res = 1;
|
|
auto dims = pybind11::detail::array_proxy(numpy_data)->dimensions;
|
|
auto nd = pybind11::detail::array_proxy(numpy_data)->nd;
|
|
while (nd--) {
|
|
res *= (*dims++);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
class EagerNumpyAllocation : public phi::Allocation {
|
|
public:
|
|
explicit EagerNumpyAllocation(PyObject* numpy_data, DataType dtype)
|
|
: Allocation(
|
|
static_cast<void*>(pybind11::detail::array_proxy(numpy_data)->data),
|
|
phi::SizeOf(dtype) * PyArray_Size_(numpy_data),
|
|
CPUPlace()),
|
|
arr_(numpy_data) {
|
|
PADDLE_ENFORCE_NOT_NULL(
|
|
arr_,
|
|
common::errors::InvalidArgument("The underlying PyObject pointer of "
|
|
"numpy array cannot be nullptr"));
|
|
PADDLE_ENFORCE_NE(
|
|
arr_,
|
|
Py_None,
|
|
common::errors::PreconditionNotMet(
|
|
"The underlying PyObject pointer of numpy array cannot be None"));
|
|
Py_INCREF(arr_);
|
|
}
|
|
~EagerNumpyAllocation() override { // NOLINT
|
|
py::gil_scoped_acquire gil;
|
|
Py_DECREF(arr_);
|
|
}
|
|
|
|
private:
|
|
PyObject* arr_;
|
|
};
|
|
|
|
static PyObject* eager_api_scale(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
// TODO(jiabin): Sync Tensor and Variable here when we support
|
|
|
|
auto& tensor =
|
|
reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor;
|
|
float scale = CastPyArg2AttrFloat(PyTuple_GET_ITEM(args, 1), 1);
|
|
float bias = CastPyArg2AttrFloat(PyTuple_GET_ITEM(args, 2), 2);
|
|
bool bias_after_scale = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 3), 3);
|
|
bool trace_backward = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 4), 4);
|
|
Tensor ret;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
ret = egr::scale(tensor, scale, bias, bias_after_scale, trace_backward);
|
|
}
|
|
return ToPyObject(ret);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_run_backward(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
auto grad_tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 1), 1);
|
|
bool retain_graph = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 2), 2);
|
|
bool create_graph = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 3), 3);
|
|
std::string dump_backward_graph_path =
|
|
CastPyArg2AttrString(PyTuple_GET_ITEM(args, 4), 4);
|
|
const phi::distributed::ProcessMesh* mesh = nullptr;
|
|
if (InputsContainDistTensor(&mesh, tensors, grad_tensors)) {
|
|
tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0, mesh);
|
|
grad_tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 1), 1, mesh);
|
|
}
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
egr::Backward(tensors,
|
|
grad_tensors,
|
|
retain_graph,
|
|
create_graph,
|
|
dump_backward_graph_path);
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_run_partial_grad(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
auto inputs = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 1), 1);
|
|
auto grad_tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 2), 2);
|
|
auto retain_graph = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 3), 3);
|
|
auto create_graph = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 4), 4);
|
|
auto only_inputs = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 5), 5);
|
|
auto allow_unused = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 6), 6);
|
|
auto no_grad_vars = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 7), 7);
|
|
auto dump_backward_graph_path =
|
|
CastPyArg2AttrString(PyTuple_GET_ITEM(args, 8), 8);
|
|
const phi::distributed::ProcessMesh* mesh = nullptr;
|
|
if (InputsContainDistTensor(
|
|
&mesh, tensors, inputs, grad_tensors, no_grad_vars)) {
|
|
tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0, mesh);
|
|
inputs = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 1), 1, mesh);
|
|
grad_tensors = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 2), 2, mesh);
|
|
no_grad_vars = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 7), 7, mesh);
|
|
}
|
|
|
|
std::vector<Tensor> result;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
result = egr::Grad(tensors,
|
|
inputs,
|
|
grad_tensors,
|
|
retain_graph,
|
|
create_graph,
|
|
only_inputs,
|
|
allow_unused,
|
|
no_grad_vars,
|
|
dump_backward_graph_path);
|
|
VLOG(4) << " in eager_api_run_partial_grad, after running egr::Grad";
|
|
}
|
|
return ToPyObject(result, true /* return_py_none_if_not_initialize */);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_tensor_copy(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
Tensor& src =
|
|
reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 0))->tensor;
|
|
Tensor& dst =
|
|
reinterpret_cast<TensorObject*>(PyTuple_GET_ITEM(args, 1))->tensor;
|
|
auto place = CastPyArg2Place(PyTuple_GET_ITEM(args, 2), 2);
|
|
bool blocking = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 3), 3);
|
|
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
dst = src.copy_to(place, blocking);
|
|
egr::EagerUtils::autograd_meta(&dst)->SetStopGradient(
|
|
egr::EagerUtils::autograd_meta(&(src))->StopGradient());
|
|
egr::EagerUtils::autograd_meta(&dst)->SetPersistable(
|
|
egr::EagerUtils::autograd_meta(&(src))->Persistable());
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager_api_get_all_grads(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensor_list = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
|
|
std::vector<Tensor> ret;
|
|
for (auto& tensor : tensor_list) {
|
|
VLOG(6) << "Get grad for tensor: " << tensor.name();
|
|
auto meta = egr::EagerUtils::nullable_autograd_meta(tensor);
|
|
if (!meta || meta->StopGradient()) {
|
|
ret.emplace_back(Tensor());
|
|
continue;
|
|
}
|
|
if (meta && meta->Grad().has_allocation()) {
|
|
ret.emplace_back(meta->Grad());
|
|
} else {
|
|
ret.emplace_back(Tensor());
|
|
}
|
|
}
|
|
return ToPyObject(ret, true);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager_api_get_grads_lists(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensor_list = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
// The order of the 3 vectors is: FP16_grads, BF16_grads, FP32_grads
|
|
std::vector<std::vector<Tensor>> ret(3);
|
|
|
|
for (auto& tensor : tensor_list) {
|
|
VLOG(6) << "Get grad for tensor: " << tensor.name();
|
|
auto meta = egr::EagerUtils::nullable_autograd_meta(tensor);
|
|
if (meta && meta->Grad().has_allocation()) {
|
|
auto& grad = meta->Grad();
|
|
switch (grad.dtype()) {
|
|
case DataType::FLOAT16:
|
|
ret[0].emplace_back(grad);
|
|
break;
|
|
case DataType::BFLOAT16:
|
|
ret[1].emplace_back(grad);
|
|
break;
|
|
case DataType::FLOAT32:
|
|
ret[2].emplace_back(grad);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ToPyObject(ret);
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager_api_get_grads_types(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensor_list = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
|
|
std::vector<DataType> ret;
|
|
|
|
for (auto& tensor : tensor_list) {
|
|
VLOG(6) << "Get grad for tensor: " << tensor.name();
|
|
auto meta = egr::EagerUtils::nullable_autograd_meta(tensor);
|
|
if (!meta || meta->StopGradient()) {
|
|
ret.emplace_back(DataType::UNDEFINED);
|
|
continue;
|
|
}
|
|
|
|
auto& grad = meta->Grad();
|
|
if (meta && grad.has_allocation()) {
|
|
if ((grad.is_dense_tensor() || grad.is_dist_tensor()) &&
|
|
(tensor.dtype() == DataType::FLOAT32 ||
|
|
tensor.dtype() == DataType::FLOAT16 ||
|
|
tensor.dtype() == DataType::BFLOAT16)) {
|
|
ret.emplace_back(tensor.dtype());
|
|
}
|
|
} else {
|
|
ret.emplace_back(DataType::UNDEFINED);
|
|
}
|
|
}
|
|
|
|
return ToPyObject(ret);
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_read_next_tensor_list(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto tensor_base_list =
|
|
CastPyArg2VectorOfTensorBase(PyTuple_GET_ITEM(args, 0), 0);
|
|
std::vector<Tensor> tensor_list;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
tensor_list.reserve(tensor_base_list.size());
|
|
auto func = [](phi::DenseTensor& tensor_base) {
|
|
Tensor tensor(egr::Controller::Instance().GenerateUniqueName());
|
|
auto autograd_meta = egr::EagerUtils::autograd_meta(&tensor);
|
|
autograd_meta->SetPersistable(false);
|
|
autograd_meta->SetStopGradient(true);
|
|
tensor.set_impl(std::make_shared<DenseTensor>(tensor_base));
|
|
return tensor;
|
|
};
|
|
for (auto& tensor_base : tensor_base_list) {
|
|
tensor_list.emplace_back(func(tensor_base));
|
|
}
|
|
}
|
|
return ToPyObject(tensor_list);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static void ConstructFwdAndBwdMap(
|
|
const std::vector<paddle::OpMetaInfo>& vec_map,
|
|
const std::string& op_type) {
|
|
auto& in_out_map = egr::Controller::Instance().GetCustomEdgesSlotMap();
|
|
if (in_out_map.find(op_type) != in_out_map.end()) {
|
|
VLOG(7) << "Find Exist CustomEdgesSlotMap Skip >>>> ";
|
|
return;
|
|
} else {
|
|
VLOG(7) << "Construct CustomEdgesSlotMap ";
|
|
auto inputs_names = paddle::OpMetaInfoHelper::GetInputs(vec_map[0]);
|
|
auto outputs_names = paddle::OpMetaInfoHelper::GetOutputs(vec_map[0]);
|
|
auto attrs_names = paddle::OpMetaInfoHelper::GetAttrs(vec_map[0]);
|
|
auto grad_outputs_names = paddle::OpMetaInfoHelper::GetOutputs(vec_map[1]);
|
|
auto grad_inputs_names = paddle::OpMetaInfoHelper::GetInputs(vec_map[1]);
|
|
auto grad_attrs_names = paddle::OpMetaInfoHelper::GetAttrs(vec_map[1]);
|
|
std::vector<std::unordered_map<int, int>> res(5);
|
|
|
|
in_out_map.insert({op_type, {res}});
|
|
// Prepare pos map for grad_outputs
|
|
VLOG(7) << "Prepare pos map for grad_outputs";
|
|
PADDLE_ENFORCE_LE(
|
|
grad_outputs_names.size(),
|
|
inputs_names.size(),
|
|
common::errors::InvalidArgument(
|
|
"Grad outputs num should be less equal than forward inputs num."));
|
|
for (size_t i = 0; i < grad_outputs_names.size(); i++) {
|
|
size_t end = grad_outputs_names[i].find("@GRAD");
|
|
PADDLE_ENFORCE_NE(
|
|
end,
|
|
std::string::npos,
|
|
common::errors::NotFound(
|
|
"All Grad outputs should be grad and we got %s is not grad var, "
|
|
"please check your op and change to fit the rule.",
|
|
grad_outputs_names[i]));
|
|
for (size_t j = 0; j < inputs_names.size(); j++) {
|
|
if (grad_outputs_names[i].substr(0, end) == inputs_names[j]) {
|
|
VLOG(7) << " ==== Custom Operator: " << op_type << "'s No." << j
|
|
<< " inputs: " << inputs_names[j] << " related to No." << i
|
|
<< " grad_outputs: " << grad_outputs_names[i];
|
|
in_out_map[op_type][0][0][j] = i; // NOLINT
|
|
}
|
|
}
|
|
}
|
|
// Prepare pos map for grad_inputs
|
|
for (size_t i = 0; i < grad_inputs_names.size(); i++) {
|
|
size_t end = grad_inputs_names[i].find("@GRAD");
|
|
if (end != std::string::npos) {
|
|
for (size_t j = 0; j < outputs_names.size(); j++) {
|
|
if (grad_inputs_names[i].substr(0, end) == outputs_names[j]) {
|
|
VLOG(7) << " ==== Custom Operator: " << op_type << "'s No." << j
|
|
<< " outputs: " << outputs_names[j] << " related to No."
|
|
<< i << " grad_inputs's grad: " << grad_inputs_names[i];
|
|
in_out_map[op_type][0][1][j] = i; // NOLINT
|
|
}
|
|
}
|
|
} else {
|
|
if (std::find(outputs_names.begin(),
|
|
outputs_names.end(),
|
|
grad_inputs_names[i]) != outputs_names.end()) {
|
|
for (size_t j = 0; j < outputs_names.size(); j++) {
|
|
if (grad_inputs_names[i] == outputs_names[j]) {
|
|
VLOG(7) << " ==== Custom Operator: " << op_type << "'s No." << j
|
|
<< " outputs: " << outputs_names[j] << " related to No."
|
|
<< i
|
|
<< " grad_inputs fwd outputs: " << grad_inputs_names[i];
|
|
in_out_map[op_type][0][2][j] = i; // NOLINT
|
|
}
|
|
}
|
|
} else {
|
|
for (size_t j = 0; j < inputs_names.size(); j++) {
|
|
if (grad_inputs_names[i] == inputs_names[j]) {
|
|
VLOG(7) << " ==== Custom Operator: " << op_type << "'s No." << j
|
|
<< " inputs: " << inputs_names[j] << " related to No."
|
|
<< i
|
|
<< " grad_inputs fwd inputs: " << grad_inputs_names[i];
|
|
in_out_map[op_type][0][3][j] = i; // NOLINT
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare pos map for grad attrs_
|
|
for (size_t i = 0; i < grad_attrs_names.size(); i++) {
|
|
auto end = std::find(
|
|
attrs_names.begin(), attrs_names.end(), grad_attrs_names[i]);
|
|
PADDLE_ENFORCE_NE(end,
|
|
attrs_names.end(),
|
|
common::errors::NotFound(
|
|
"All Grad attrs should be one of forward attrs and "
|
|
"we got %s is not one of them, please check your "
|
|
"op and change to fit the rule.",
|
|
grad_attrs_names[i]));
|
|
for (size_t j = 0; j < attrs_names.size(); j++) {
|
|
if (grad_attrs_names[i] == attrs_names[j]) {
|
|
VLOG(7) << " ==== Custom Operator: " << op_type << "'s No." << j
|
|
<< " attrs: " << attrs_names[j] << " related to No." << i
|
|
<< " grad_attrs: " << grad_attrs_names[i];
|
|
in_out_map[op_type][0][4][j] = i; // NOLINT
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static PyObject* eager_api_jit_function_call(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
|
|
std::shared_ptr<jit::Function> function =
|
|
CastPyArg2JitFunction(PyTuple_GET_ITEM(args, 0), 0);
|
|
std::vector<Tensor> ins =
|
|
CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 1), 1);
|
|
std::vector<Tensor> outs;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
outs = (*function)(ins);
|
|
}
|
|
return ToPyObject(outs);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api__get_custom_operator_inplace_reverse_idx(
|
|
PyObject* self, PyObject* args, PyObject* kwargs) {
|
|
EAGER_TRY
|
|
std::string op_type = CastPyArg2AttrString(PyTuple_GET_ITEM(args, 0), 0);
|
|
auto meta_info_map = egr::Controller::Instance().GetOpMetaInfoMap();
|
|
PADDLE_ENFORCE_NE(meta_info_map.find(op_type),
|
|
meta_info_map.end(),
|
|
common::errors::NotFound(
|
|
"Can't find %s in Eager OpMetaInfoMap which should be "
|
|
"created by LoadOpMetaInfoAndRegisterOp, please make "
|
|
"sure you registered your op first and try again. ",
|
|
op_type));
|
|
|
|
const auto& inputs =
|
|
paddle::OpMetaInfoHelper::GetInputs(meta_info_map.at(op_type)[0]);
|
|
const auto& outputs =
|
|
paddle::OpMetaInfoHelper::GetOutputs(meta_info_map.at(op_type)[0]);
|
|
const auto& inplace_map =
|
|
paddle::OpMetaInfoHelper::GetInplaceMap(meta_info_map.at(op_type)[0]);
|
|
VLOG(7) << "Custom operator " << op_type
|
|
<< " get InplaceMap for python, inplace map size = "
|
|
<< inplace_map.size();
|
|
|
|
std::unordered_map<int, int> inplace_idx_map;
|
|
for (size_t in_idx = 0; in_idx < inputs.size(); ++in_idx) {
|
|
auto& input = inputs[in_idx];
|
|
if (inplace_map.find(input) == inplace_map.end()) {
|
|
continue;
|
|
}
|
|
auto out_iter = find(outputs.begin(), outputs.end(), inplace_map.at(input));
|
|
PADDLE_ENFORCE(out_iter != outputs.end(),
|
|
common::errors::NotFound(
|
|
"Can't find the mapped value of %s, please check "
|
|
"the input of `Inplace` again and make "
|
|
"sure you registered your op accurately. ",
|
|
input));
|
|
inplace_idx_map[distance(outputs.begin(), out_iter)] = in_idx; // NOLINT
|
|
}
|
|
|
|
return ToPyObject(inplace_idx_map);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
// This function copies from function `EmptyTensorInitializer` with default
|
|
// parameters
|
|
static Tensor InitializedEmptyTensor() {
|
|
auto ddims = common::make_ddim({0});
|
|
auto tensor = Tensor();
|
|
tensor.set_name(
|
|
egr::Controller::Instance().GenerateUniqueName("generated_tensor"));
|
|
auto autograd_meta = egr::EagerUtils::autograd_meta(&tensor);
|
|
autograd_meta->SetPersistable(false);
|
|
std::shared_ptr<DenseTensor> dense_tensor = nullptr;
|
|
std::shared_ptr<phi::Allocation> allocation_ptr = nullptr;
|
|
dense_tensor = std::make_shared<DenseTensor>(
|
|
allocation_ptr, phi::DenseTensorMeta(DataType::FLOAT32, ddims));
|
|
tensor.set_impl(dense_tensor);
|
|
autograd_meta->SetGradNode(
|
|
std::make_shared<egr::GradNodeAccumulation>(tensor));
|
|
return tensor;
|
|
}
|
|
|
|
PyObject* eager_api_run_custom_op(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
FLAGS_tensor_operants_mode = "phi";
|
|
bool compact_flag_bak = FLAGS_enable_compact_mem;
|
|
FLAGS_enable_compact_mem = false;
|
|
if (paddle::OperantsManager::Instance().phi_operants.get() == nullptr) {
|
|
paddle::OperantsManager::Instance().phi_operants =
|
|
std::make_unique<paddle::operants::PhiTensorOperants>();
|
|
VLOG(4) << "Initialize phi tensor operants successfully";
|
|
}
|
|
|
|
std::string op_type = CastPyArg2AttrString(PyTuple_GET_ITEM(args, 0), 0);
|
|
VLOG(7) << "Get things from python for Custom Op: " << op_type;
|
|
if (FLAGS_check_cuda_error) [[unlikely]] {
|
|
egr::CUDAErrorCheck("eager_api_run_custom_op " + op_type + " begin");
|
|
}
|
|
std::string unique_api_name;
|
|
if (VLOG_IS_ON(3) || FLAGS_enable_unique_name) {
|
|
static int64_t call_count = 0;
|
|
call_count++;
|
|
unique_api_name =
|
|
egr::GenerateUniqueApiName("custom_op_" + op_type, call_count);
|
|
}
|
|
paddle::CustomOpKernelContext ctx;
|
|
auto meta_info_map = egr::Controller::Instance().GetOpMetaInfoMap();
|
|
PADDLE_ENFORCE_NE(meta_info_map.find(op_type),
|
|
meta_info_map.end(),
|
|
common::errors::NotFound(
|
|
"Can't find %s in Eager OpMetaInfoMap which should be "
|
|
"created by LoadOpMetaInfoAndRegisterOp, please make "
|
|
"sure you registered your op first and try again. ",
|
|
op_type));
|
|
const auto& vec_map = meta_info_map.at(op_type);
|
|
const auto& inputs = paddle::OpMetaInfoHelper::GetInputs(vec_map[0]);
|
|
const auto& attrs = paddle::OpMetaInfoHelper::GetAttrs(vec_map[0]);
|
|
const auto& outputs = paddle::OpMetaInfoHelper::GetOutputs(vec_map[0]);
|
|
const auto& inplace_map = paddle::OpMetaInfoHelper::GetInplaceMap(vec_map[0]);
|
|
SetPythonStack();
|
|
for (size_t i = 0; i < inputs.size(); ++i) {
|
|
const auto& input = inputs.at(i);
|
|
// Parse op_type first, so that use i + 1
|
|
PyObject* obj = PyTuple_GET_ITEM(args, i + 1);
|
|
// Emplace Py_None from python, this means optional inputs passed to C++,
|
|
// use one un-initialized tensor to indicate both Tensor and
|
|
// vector<Tensor> inputs.
|
|
if (obj == Py_None) {
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add un-initialized tensor "
|
|
"because the optional input is None";
|
|
ctx.EmplaceBackInput(Tensor());
|
|
continue;
|
|
}
|
|
if (framework::detail::IsDuplicableVar(input)) {
|
|
std::vector<Tensor> tensors = CastPyArg2VectorOfTensor(obj, i + 1);
|
|
ctx.EmplaceBackInputs(std::move(tensors));
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add vector<Tensor> size = "
|
|
<< ctx.InputRangeAt(i).second - ctx.InputRangeAt(i).first;
|
|
} else {
|
|
const Tensor& tensor = CastPyArg2Tensor(obj, i + 1); // NOLINT
|
|
ctx.EmplaceBackInput(tensor);
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add Tensor for general case.";
|
|
}
|
|
}
|
|
|
|
const phi::distributed::ProcessMesh* mesh = nullptr;
|
|
if (InputsContainDistTensor(&mesh, *(ctx.AllMutableInput()))) {
|
|
paddle::CustomOpKernelContext empty_ctx;
|
|
ctx = empty_ctx;
|
|
for (size_t i = 0; i < inputs.size(); ++i) {
|
|
const auto& input = inputs.at(i);
|
|
// Parse op_type first, so that use i + 1
|
|
PyObject* obj = PyTuple_GET_ITEM(args, i + 1);
|
|
// Emplace Py_None from python, this means optional inputs passed to C++,
|
|
// use one un-initialized tensor to indicate both Tensor and
|
|
// vector<Tensor> inputs.
|
|
if (obj == Py_None) {
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add un-initialized tensor "
|
|
"because the optional input is None";
|
|
ctx.EmplaceBackInput(Tensor());
|
|
continue;
|
|
}
|
|
if (framework::detail::IsDuplicableVar(input)) {
|
|
std::vector<Tensor> tensors =
|
|
CastPyArg2VectorOfTensor(obj, i + 1, mesh);
|
|
ctx.EmplaceBackInputs(std::move(tensors));
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add vector<Tensor> size = "
|
|
<< ctx.InputRangeAt(i).second - ctx.InputRangeAt(i).first;
|
|
} else {
|
|
Tensor& tensor = CastPyArg2Tensor(obj, i + 1); // NOLINT
|
|
ConvertAllInputsToDistTensor(mesh, tensor);
|
|
ctx.EmplaceBackInput(tensor);
|
|
VLOG(7) << "Custom operator add input " << input
|
|
<< " to CustomOpKernelContext. Add Tensor for general case.";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse op_type and inputs first, so that use 1 + inputs.size() + i
|
|
int attr_start_idx = static_cast<int>(1 + inputs.size());
|
|
for (size_t i = 0; i < attrs.size(); ++i) {
|
|
const auto& attr = attrs.at(i);
|
|
std::vector<std::string> attr_name_and_type = paddle::ParseAttrStr(attr);
|
|
auto attr_type_str = attr_name_and_type[1];
|
|
VLOG(7) << "Custom operator add attrs " << attr_name_and_type[0]
|
|
<< " to CustomOpKernelContext. Attribute type = " << attr_type_str;
|
|
PyObject* obj = PyTuple_GET_ITEM(args, attr_start_idx + i);
|
|
if (attr_type_str == "bool") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2AttrBoolean(obj, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "int") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2AttrInt(obj, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "float") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2AttrFloat(obj, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "double") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2AttrDouble(obj, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "int64_t") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2Long(obj, op_type, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "std::string") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2AttrString(obj, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "std::vector<int>") { // NOLINT
|
|
ctx.EmplaceBackAttr(CastPyArg2VectorOfInt(obj, attr_start_idx + i));
|
|
} else if (attr_type_str == "std::vector<float>") {
|
|
ctx.EmplaceBackAttr(CastPyArg2VectorOfFloat(obj, attr_start_idx + i));
|
|
} else if (attr_type_str == "std::vector<int64_t>") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2Longs(obj, op_type, attr_start_idx + i)); // NOLINT
|
|
} else if (attr_type_str == "std::vector<std::string>") {
|
|
ctx.EmplaceBackAttr(
|
|
CastPyArg2VectorOfString(obj, attr_start_idx + i)); // NOLINT
|
|
} else {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"Unsupported `%s` type value as custom attribute now. "
|
|
"Supported data types include `bool`, `int`, `float`, "
|
|
"`int64_t`, `std::string`, `std::vector<int>`, "
|
|
"`std::vector<float>`, `std::vector<int64_t>`, "
|
|
"`std::vector<std::string>`, Please check whether "
|
|
"the attribute data type and data type string are matched.",
|
|
attr_type_str));
|
|
}
|
|
}
|
|
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
|
|
// Check LeafTensor if its GradNodeAccumulation TensorMeta is consistent
|
|
// with its TensorMeta
|
|
egr::CheckGradNodeAccumulation(*ctx.AllMutableInput());
|
|
|
|
ctx.ConstructInplaceIndex(inputs, outputs, inplace_map);
|
|
const auto& inplace_reverse_idx_map = ctx.GetInplaceReverseIndexMap();
|
|
for (size_t out_idx = 0; out_idx < outputs.size(); ++out_idx) {
|
|
const auto& output = outputs.at(out_idx);
|
|
// inplace special case
|
|
if (inplace_reverse_idx_map.find(out_idx) !=
|
|
inplace_reverse_idx_map.end()) {
|
|
size_t in_idx = inplace_reverse_idx_map.at(out_idx);
|
|
const auto& input_range = ctx.InputRangeAt(in_idx);
|
|
const auto& input_tensor = ctx.InputAt(input_range.first);
|
|
// inplace optional [Tensor or vector<Tensor>], un-initialized tensor.
|
|
if (framework::detail::IsOptionalVar(output) &&
|
|
!input_tensor.has_allocation()) {
|
|
VLOG(7) << "Custom operator add output " << output
|
|
<< " to CustomOpKernelContext. Add un-initialized tensor "
|
|
"because the inplace optional input is None";
|
|
ctx.EmplaceBackOutput(Tensor());
|
|
continue;
|
|
}
|
|
/// inplace vector<Tensor>, initialized tensor.
|
|
if (framework::detail::IsDuplicableVar(output)) {
|
|
std::vector<Tensor> empty_tensors;
|
|
size_t vector_size = input_range.second - input_range.first;
|
|
empty_tensors.resize(vector_size);
|
|
for (size_t i = 0; i < vector_size; ++i) {
|
|
empty_tensors[i] = InitializedEmptyTensor();
|
|
}
|
|
VLOG(7) << "Custom operator add output " << output
|
|
<< " to CustomOpKernelContext. Add vector<tensor> size = "
|
|
<< empty_tensors.size();
|
|
ctx.EmplaceBackOutputs(empty_tensors);
|
|
continue;
|
|
}
|
|
}
|
|
VLOG(7) << "Custom operator add output " << output
|
|
<< " to CustomOpKernelContext. Add initialized Tensor because "
|
|
"using general or inplace mechanism";
|
|
// general Tensor or inplace Tensor, initialized tensor.
|
|
ctx.EmplaceBackOutput(InitializedEmptyTensor());
|
|
}
|
|
|
|
VLOG(7) << "Run Kernel of Custom Op: " << op_type;
|
|
egr::run_custom_op_impl(vec_map[0], true, false, ctx);
|
|
|
|
// handle optional None output when construct backward graph
|
|
for (size_t i = 0; i < ctx.OutputRange().size(); i++) {
|
|
if (ctx.OutputRangeAt(i).first + 1 == ctx.OutputRangeAt(i).second) {
|
|
Tensor* out_tensor = ctx.MutableOutputAt(ctx.OutputRangeAt(i).first);
|
|
if (!out_tensor->has_allocation()) {
|
|
PADDLE_ENFORCE(
|
|
framework::detail::IsOptionalVar(outputs.at(i)) ||
|
|
out_tensor->is_dist_tensor(),
|
|
common::errors::InvalidArgument(
|
|
"Custom operator[%s]'s %d-th output is not initialized. "
|
|
"Please check your implementation again. If you are "
|
|
"using inplace optional output, then you must use "
|
|
"`paddle::Optional` to decorate this output",
|
|
op_type,
|
|
i));
|
|
// We can also consider using `autograd_meta` to tolerant nullptr.
|
|
out_tensor->set_autograd_meta(std::make_shared<egr::AutogradMeta>());
|
|
}
|
|
if (out_tensor) {
|
|
// Set unique name
|
|
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
|
|
egr::SetTensorName(
|
|
unique_api_name, "out_" + std::to_string(i), out_tensor);
|
|
}
|
|
// Save the tensors checksum to file_path
|
|
if (!FLAGS_tensor_md5_checksum_output_path.empty()) {
|
|
egr::SaveTensorMD5CheckSumToFile(
|
|
FLAGS_tensor_md5_checksum_output_path, *out_tensor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VLOG(7) << "Get AutogradMeta for inputs and outputs for Custom Op";
|
|
size_t slot_ins_num = ctx.InputRange().size();
|
|
size_t slot_outs_num = ctx.OutputRange().size();
|
|
VLOG(7) << "We got slot num of ins is: " << slot_ins_num;
|
|
VLOG(7) << "We got slot num of outs is: " << slot_outs_num;
|
|
std::vector<egr::AutogradMeta*> ins_auto_grad_metas =
|
|
egr::EagerUtils::nullable_autograd_meta(*ctx.AllMutableInput());
|
|
std::vector<egr::AutogradMeta*> outs_auto_grad_metas =
|
|
egr::EagerUtils::unsafe_autograd_meta(*ctx.AllMutableOutput());
|
|
|
|
bool require_any_grad = false;
|
|
bool trace_backward = true;
|
|
for (size_t i = 0; i < ins_auto_grad_metas.size(); ++i) {
|
|
require_any_grad =
|
|
require_any_grad || egr::EagerUtils::ComputeRequireGrad(
|
|
trace_backward, ins_auto_grad_metas[i]);
|
|
}
|
|
|
|
// handle inplace map
|
|
if (!inplace_map.empty()) {
|
|
for (size_t i = 0; i < ctx.InputRange().size(); i++) {
|
|
if (inplace_map.find(inputs[i]) == inplace_map.end()) {
|
|
continue;
|
|
}
|
|
const auto& input_pair = ctx.InputRangeAt(i);
|
|
for (size_t j = input_pair.first; j < input_pair.second; j++) {
|
|
egr::EagerUtils::CheckInplace(
|
|
ctx.InputAt(j), ins_auto_grad_metas[j], require_any_grad);
|
|
if (ctx.MutableInputAt(j).defined()) {
|
|
// Bump Inplace Version
|
|
ctx.MutableInputAt(j).bump_inplace_version();
|
|
VLOG(3) << "Custom operator: Tensor(" << ctx.InputAt(j).name()
|
|
<< ") uses Inplace Strategy.";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (require_any_grad && (vec_map.size() > 1)) {
|
|
VLOG(6) << " Construct Grad for Custom Op: " << op_type;
|
|
ConstructFwdAndBwdMap(vec_map, op_type);
|
|
for (auto& outs_auto_grad_meta : outs_auto_grad_metas) {
|
|
egr::EagerUtils::PassStopGradient(false, outs_auto_grad_meta);
|
|
}
|
|
// Note(HongyuJia): In dygraph eager mode, CheckInplace makes sure leaf
|
|
// nodes set stop_gradient=True. However, dygraph mode can also outputs
|
|
// lead nodes' gradients (For example, we can get x.grad after x.add_(y)).
|
|
// To be consistent with dygraph mode, we have to PassStopGradient for all
|
|
// inplaced ins_auto_grad_metas.
|
|
const auto& inplace_index_map = ctx.GetInplaceIndexMap();
|
|
for (auto pair : inplace_index_map) {
|
|
const auto& size_pair = ctx.InputRangeAt(pair.first);
|
|
for (size_t i = size_pair.first; i < size_pair.second; ++i) {
|
|
egr::EagerUtils::PassStopGradient(false, ins_auto_grad_metas[i]);
|
|
}
|
|
}
|
|
auto grad_node = std::make_shared<egr::RunCustomOpNode>(
|
|
slot_outs_num, slot_ins_num, op_type);
|
|
const auto& slot_map =
|
|
egr::Controller::Instance().GetCustomEdgesSlotMap().at(op_type);
|
|
|
|
// Set for Record Subgraph
|
|
if (egr::EagerBackwardSubGraphNodeRecorder::Instance()
|
|
.NeedCaptureSubGraph()) {
|
|
VLOG(3) << "Capture the grad node" << grad_node->name() << "("
|
|
<< grad_node.get() << ")"
|
|
<< "for subgraph.";
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance().AddGradNode(
|
|
grad_node.get());
|
|
}
|
|
// Set GradNode name
|
|
if (VLOG_IS_ON(6) || FLAGS_enable_unique_name) {
|
|
// Set GradNodeName
|
|
grad_node->SetNameFromAPI(unique_api_name);
|
|
}
|
|
// Prepare Grad outputs
|
|
size_t no_grad_cnt = 0;
|
|
for (size_t i = 0; i < slot_ins_num; i++) {
|
|
const std::vector<Tensor>& in_tensors = ctx.InputsBetween(
|
|
ctx.InputRangeAt(i).first, ctx.InputRangeAt(i).second);
|
|
|
|
if (slot_map[0][0].find(static_cast<int>(i)) != slot_map[0][0].end()) {
|
|
grad_node->SetGradOutMeta(in_tensors,
|
|
slot_map[0][0].at(static_cast<int>(i)));
|
|
} else {
|
|
grad_node->SetGradOutMeta(in_tensors, slot_ins_num - 1 - no_grad_cnt);
|
|
no_grad_cnt++;
|
|
}
|
|
}
|
|
// Prepare Grad inputs with grad of fwd outputs
|
|
for (size_t i = 0; i < slot_outs_num; i++) {
|
|
const auto& size_pair = ctx.OutputRangeAt(i);
|
|
const std::vector<Tensor>& out_tensors =
|
|
ctx.OutputsBetween(size_pair.first, size_pair.second);
|
|
for (size_t j = size_pair.first; j < size_pair.second; j++) {
|
|
// SetOutRankWithSlot: slot_id = i, rank = j - size_pair.first
|
|
outs_auto_grad_metas[j]->SetSingleOutRankWithSlot(
|
|
i, j - size_pair.first);
|
|
egr::EagerUtils::SetHistory(outs_auto_grad_metas[j], grad_node);
|
|
}
|
|
grad_node->SetGradInMeta(out_tensors, i);
|
|
}
|
|
|
|
// Prepare Grad inputs with fwd outputs
|
|
for (auto item : slot_map[0][2]) {
|
|
VLOG(7) << "Prepare fwd_outs: " << item.first
|
|
<< " to grad_inputs: " << item.second;
|
|
grad_node->fwd_outs[item.second] =
|
|
egr::RunCustomOpNode::ConstructTensorWrapper(
|
|
ctx.OutputsBetween(ctx.OutputRangeAt(item.first).first,
|
|
ctx.OutputRangeAt(item.first).second));
|
|
}
|
|
|
|
// Prepare Grad inputs with fwd inputs
|
|
for (auto item : slot_map[0][3]) {
|
|
VLOG(7) << "Prepare fwd_ins: " << item.first
|
|
<< " to grad_inputs: " << item.second;
|
|
grad_node->fwd_ins[item.second] =
|
|
egr::RunCustomOpNode::ConstructTensorWrapper(
|
|
ctx.InputsBetween(ctx.InputRangeAt(item.first).first,
|
|
ctx.InputRangeAt(item.first).second));
|
|
}
|
|
|
|
const std::vector<paddle::any>& res_attrs = ctx.Attrs();
|
|
std::vector<paddle::any> attrs(res_attrs.size());
|
|
// Prepare attrs for Grad node
|
|
for (auto item : slot_map[0][4]) {
|
|
VLOG(7) << "Prepare fwd attrs: " << item.first
|
|
<< " to grad_attrs: " << item.second;
|
|
attrs[item.second] = res_attrs[item.first];
|
|
}
|
|
grad_node->SetAttrs(attrs);
|
|
}
|
|
}
|
|
if (FLAGS_check_cuda_error) [[unlikely]] {
|
|
egr::CUDAErrorCheck("eager_api_run_custom_op " + op_type + " finish");
|
|
}
|
|
if (VLOG_IS_ON(3) && FLAGS_enable_unique_name) {
|
|
const char* INPUT_PRINT_TEMPLATE =
|
|
"\nForward Debug Info {\nAPI_Name: %s \nInput: [%s] \nOutput: [%s] } ";
|
|
std::string input_str = "";
|
|
std::string output_str = "";
|
|
const char* TENSOR_INPUT_TEMPLATE = " \n( input , %s), ";
|
|
input_str = paddle::string::Sprintf(
|
|
TENSOR_INPUT_TEMPLATE,
|
|
egr::EagerUtils::TensorStr(*ctx.AllMutableInput()));
|
|
const char* TENSOR_OUT_TEMPLATE = " \n( out, %s), ";
|
|
output_str = paddle::string::Sprintf(
|
|
TENSOR_OUT_TEMPLATE,
|
|
egr::EagerUtils::TensorStr(*ctx.AllMutableOutput()));
|
|
VLOG(3) << paddle::string::Sprintf(
|
|
INPUT_PRINT_TEMPLATE, unique_api_name, input_str, output_str);
|
|
}
|
|
FLAGS_enable_compact_mem = compact_flag_bak;
|
|
return ToPyObject(*ctx.AllMutableOutput());
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_sparse_coo_tensor(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto non_zero_indices = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
auto non_zero_elements = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 1), 1);
|
|
auto dense_shape = CastPyArg2VectorOfInt(PyTuple_GET_ITEM(args, 2), 2);
|
|
auto stop_gradient = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 3), 3);
|
|
Tensor tensor;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
PADDLE_ENFORCE(
|
|
non_zero_indices.is_dense_tensor(),
|
|
common::errors::Fatal("the non-zero indices must be a DenseTensor."));
|
|
PADDLE_ENFORCE(
|
|
non_zero_elements.is_dense_tensor(),
|
|
common::errors::Fatal("the non-zero elements must be a DenseTensor."));
|
|
auto dense_indices =
|
|
std::dynamic_pointer_cast<DenseTensor>(non_zero_indices.impl());
|
|
auto dense_elements =
|
|
std::dynamic_pointer_cast<DenseTensor>(non_zero_elements.impl());
|
|
// TODO(zhangkaihuo): After creating SparseCooTensor, call coalesced() to
|
|
// sort and merge duplicate indices
|
|
std::shared_ptr<phi::SparseCooTensor> coo_tensor =
|
|
std::make_shared<phi::SparseCooTensor>(
|
|
*dense_indices, *dense_elements, common::make_ddim(dense_shape));
|
|
tensor.set_impl(coo_tensor);
|
|
auto name =
|
|
egr::Controller::Instance().GenerateUniqueName("generated_tensor");
|
|
tensor.set_name(name);
|
|
auto autograd_meta = egr::EagerUtils::autograd_meta(&tensor);
|
|
autograd_meta->SetStopGradient(static_cast<bool>(stop_gradient));
|
|
if (!autograd_meta->GetMutableGradNode()) {
|
|
VLOG(3) << "Tensor(" << name
|
|
<< ") doesn't have GradNode, add GradNodeAccumulation to it.";
|
|
autograd_meta->SetGradNode(
|
|
std::make_shared<egr::GradNodeAccumulation>(tensor));
|
|
}
|
|
}
|
|
return ToPyObject(tensor);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_sparse_csr_tensor(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto non_zero_crows = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
auto non_zero_cols = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 1), 1);
|
|
auto non_zero_elements = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 2), 2);
|
|
auto dense_shape = CastPyArg2VectorOfInt(PyTuple_GET_ITEM(args, 3), 3);
|
|
auto stop_gradient = CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 4), 4);
|
|
Tensor tensor;
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
PADDLE_ENFORCE(non_zero_crows.is_dense_tensor(),
|
|
common::errors::Fatal(
|
|
"the compressed non-zero rows must be a DenseTensor."));
|
|
PADDLE_ENFORCE(
|
|
non_zero_cols.is_dense_tensor(),
|
|
common::errors::Fatal("the non-zero cols must be a DenseTensor."));
|
|
PADDLE_ENFORCE(
|
|
non_zero_elements.is_dense_tensor(),
|
|
common::errors::Fatal("the non-zero elements must be a DenseTensor."));
|
|
|
|
auto dense_crows =
|
|
std::dynamic_pointer_cast<DenseTensor>(non_zero_crows.impl());
|
|
auto dense_cols =
|
|
std::dynamic_pointer_cast<DenseTensor>(non_zero_cols.impl());
|
|
auto dense_elements =
|
|
std::dynamic_pointer_cast<DenseTensor>(non_zero_elements.impl());
|
|
std::shared_ptr<phi::SparseCsrTensor> csr_tensor =
|
|
std::make_shared<phi::SparseCsrTensor>(*dense_crows,
|
|
*dense_cols,
|
|
*dense_elements,
|
|
common::make_ddim(dense_shape));
|
|
tensor.set_impl(csr_tensor);
|
|
auto name =
|
|
egr::Controller::Instance().GenerateUniqueName("generated_tensor");
|
|
tensor.set_name(name);
|
|
auto autograd_meta = egr::EagerUtils::autograd_meta(&tensor);
|
|
autograd_meta->SetStopGradient(static_cast<bool>(stop_gradient));
|
|
if (!autograd_meta->GetMutableGradNode()) {
|
|
VLOG(3) << "Tensor(" << name
|
|
<< ") have not GradNode, add GradNodeAccumulation for it.";
|
|
autograd_meta->SetGradNode(
|
|
std::make_shared<egr::GradNodeAccumulation>(tensor));
|
|
}
|
|
}
|
|
return ToPyObject(tensor);
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_register_saved_tensors_hooks(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
if (egr::Controller::Instance().HasGrad()) {
|
|
auto pack_hook = PyTuple_GET_ITEM(args, 0);
|
|
auto unpack_hook = PyTuple_GET_ITEM(args, 1);
|
|
egr::SavedTensorsHooks::GetInstance().SetHooks(
|
|
std::make_shared<PackHook>(pack_hook),
|
|
std::make_shared<UnPackHook>(unpack_hook));
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_reset_saved_tensors_hooks(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
egr::SavedTensorsHooks::GetInstance().ResetHooks();
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
static PyObject* eager_api_print_tensor_in_gpu(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
VLOG(4) << "Running in eager_api_print_tensor_in_gpu.";
|
|
auto tensor = CastPyArg2Tensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
PADDLE_ENFORCE_EQ(tensor.is_dense_tensor(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"_print_tensor_in_gpu only supports DenseTensor."));
|
|
const auto& dense =
|
|
*static_cast<const phi::DenseTensor*>(tensor.impl().get());
|
|
PADDLE_ENFORCE_EQ(dense.place().GetType() == phi::AllocationType::GPU,
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"_print_tensor_in_gpu only supports GPU tensors. "
|
|
"Please call tensor.cuda() first."));
|
|
// ----------------------------------------------------------------
|
|
// Stream selection: must use the CUDA Graph capturing stream when
|
|
// capture is in progress. Using get_current_stream() (which may
|
|
// return the legacy stream 0) during capture would cause
|
|
// cudaErrorStreamCaptureImplicit (error 906) because CUDA forbids
|
|
// any operation on the legacy stream while another stream is being
|
|
// captured.
|
|
//
|
|
// When NOT capturing, get_current_stream gives the correct stream
|
|
// for ordering the debug kernel after preceding ops.
|
|
// ----------------------------------------------------------------
|
|
cudaStream_t stream = nullptr;
|
|
if (phi::backends::gpu::CUDAGraph::IsCapturing()) {
|
|
// During graph capture the DeviceContext stream IS the capturing
|
|
// stream (set in cuda_graph_with_memory_pool.cc:BeginCapture).
|
|
auto* dev_ctx = static_cast<phi::GPUContext*>(
|
|
phi::DeviceContextPool::Instance().Get(dense.place()));
|
|
stream = dev_ctx->stream();
|
|
} else {
|
|
const auto device_id = dense.place().GetDeviceId();
|
|
stream = paddle::platform::get_current_stream(device_id)->raw_stream();
|
|
}
|
|
phi::DebugPrintGPUTensor(dense, stream);
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
#endif
|
|
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
static PyObject* eager_api_async_read(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto& src = GetTensorFromArgs("async_read", "src", args, 0, false);
|
|
auto& dst = GetTensorFromArgs("async_read", "dst", args, 1, false);
|
|
auto& index = GetTensorFromArgs("async_read", "index", args, 2, false);
|
|
auto& buffer = GetTensorFromArgs("async_read", "buffer", args, 3, false);
|
|
auto& offset = GetTensorFromArgs("async_read", "offset", args, 4, false);
|
|
auto& count = GetTensorFromArgs("async_read", "count", args, 5, false);
|
|
const phi::distributed::ProcessMesh* mesh = nullptr;
|
|
if (InputsContainDistTensor(&mesh, src, dst, index, buffer, offset, count)) {
|
|
ConvertAllInputsToDistTensor(mesh, src, dst, index, buffer, offset, count);
|
|
}
|
|
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
PADDLE_ENFORCE_EQ(
|
|
src.is_gpu_pinned(),
|
|
true,
|
|
common::errors::InvalidArgument("Required `src` device should be "
|
|
"CUDAPinnedPlace, but received %d.",
|
|
src.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
dst.is_gpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `dst` device should be CUDAPlace, but received %d.",
|
|
dst.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
index.is_cpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `index` device should be CPUPlace, but received %d.",
|
|
index.place()));
|
|
PADDLE_ENFORCE_EQ(buffer.is_gpu_pinned(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `buffer` device should be CUDAPinnedPlace, "
|
|
"but received %d.",
|
|
buffer.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
offset.is_cpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `offset` device should be CPUPlace, but received %d.",
|
|
offset.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
count.is_cpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `count` device should be CPUPlace, but received %d.",
|
|
count.place()));
|
|
|
|
auto& src_tensor = src;
|
|
auto* dst_tensor = &dst;
|
|
auto& index_tensor = index;
|
|
auto* buffer_tensor = &buffer;
|
|
auto& offset_tensor = offset;
|
|
auto& count_tensor = count;
|
|
auto* dst_data = dst_tensor->mutable_data<float>(dst.place());
|
|
const auto& deviceId = paddle::platform::GetCurrentDeviceId();
|
|
|
|
PADDLE_ENFORCE_EQ(src_tensor.dims().size(),
|
|
dst_tensor->dims().size(),
|
|
common::errors::InvalidArgument(
|
|
"`src` and `dst` should have same tensor shape, "
|
|
"except for the first dimension."));
|
|
PADDLE_ENFORCE_EQ(src_tensor.dims().size(),
|
|
buffer_tensor->dims().size(),
|
|
common::errors::InvalidArgument(
|
|
"`src` and `buffer` should have same tensor shape, "
|
|
"except for the first dimension."));
|
|
for (int i = 1; i < src_tensor.dims().size(); i++) {
|
|
PADDLE_ENFORCE_EQ(
|
|
src_tensor.dims()[i],
|
|
dst_tensor->dims()[i],
|
|
common::errors::InvalidArgument(
|
|
"`src` and `dst` should have the same tensor shape, "
|
|
"except for the first dimension."));
|
|
PADDLE_ENFORCE_EQ(
|
|
src_tensor.dims()[i],
|
|
buffer_tensor->dims()[i],
|
|
common::errors::InvalidArgument(
|
|
"`src` and `buffer` should have the same tensor shape, "
|
|
"except for the first dimension."));
|
|
}
|
|
PADDLE_ENFORCE_EQ(index_tensor.dims().size(),
|
|
1,
|
|
common::errors::InvalidArgument(
|
|
"`index` tensor should be one-dimensional."));
|
|
|
|
auto stream = paddle::platform::get_current_stream(deviceId)->raw_stream();
|
|
|
|
int64_t numel = 0; // total copy length
|
|
int64_t copy_flag = offset_tensor.dims()[0];
|
|
int64_t size = src_tensor.numel() / src_tensor.dims()[0];
|
|
|
|
if (copy_flag != 0) {
|
|
PADDLE_ENFORCE_EQ(offset_tensor.dims().size(),
|
|
1,
|
|
common::errors::InvalidArgument(
|
|
"`offset` tensor should be one-dimensional."));
|
|
PADDLE_ENFORCE_EQ(count_tensor.dims().size(),
|
|
1,
|
|
common::errors::InvalidArgument(
|
|
"`count` tensor should be one-dimensional."));
|
|
PADDLE_ENFORCE_EQ(offset_tensor.numel(),
|
|
count_tensor.numel(),
|
|
common::errors::InvalidArgument(
|
|
"`offset` and `count` tensor size mismatch."));
|
|
auto* offset_data = offset_tensor.data<int64_t>();
|
|
auto* count_data = count_tensor.data<int64_t>();
|
|
for (int64_t i = 0; i < count_tensor.numel(); i++) {
|
|
numel += count_data[i];
|
|
}
|
|
PADDLE_ENFORCE_LE(
|
|
numel + index_tensor.numel(),
|
|
buffer_tensor->dims()[0],
|
|
common::errors::InvalidArgument("Buffer tensor size is too small."));
|
|
PADDLE_ENFORCE_LE(
|
|
numel + index_tensor.numel(),
|
|
dst_tensor->dims()[0],
|
|
common::errors::InvalidArgument("Target tensor size is too small."));
|
|
|
|
int64_t src_offset, dst_offset = 0, c;
|
|
auto* src_data = src_tensor.data<float>();
|
|
for (int64_t i = 0; i < offset_tensor.numel(); i++) {
|
|
src_offset = offset_data[i], c = count_data[i];
|
|
PADDLE_ENFORCE_LE(
|
|
src_offset + c,
|
|
src_tensor.dims()[0],
|
|
common::errors::InvalidArgument("Invalid offset or count index."));
|
|
PADDLE_ENFORCE_LE(
|
|
dst_offset + c,
|
|
dst_tensor->dims()[0],
|
|
common::errors::InvalidArgument("Invalid offset or count index."));
|
|
cudaMemcpyAsync(dst_data + (dst_offset * size),
|
|
src_data + (src_offset * size),
|
|
c * size * sizeof(float),
|
|
cudaMemcpyHostToDevice,
|
|
stream);
|
|
dst_offset += c;
|
|
}
|
|
} else {
|
|
PADDLE_ENFORCE_LE(
|
|
index_tensor.numel(),
|
|
buffer_tensor->dims()[0],
|
|
common::errors::InvalidArgument("Buffer tensor size is too small."));
|
|
}
|
|
|
|
// Select the index data to the buffer
|
|
auto index_select = [](const Tensor& src_tensor,
|
|
const Tensor& index_tensor,
|
|
Tensor* buffer_tensor) {
|
|
auto* src_data = src_tensor.data<float>();
|
|
auto* index_data = index_tensor.data<int64_t>();
|
|
auto* buffer_data = buffer_tensor->data<float>();
|
|
const int64_t slice_size =
|
|
src_tensor.numel() / src_tensor.dims()[0]; // NOLINT
|
|
const size_t copy_bytes =
|
|
static_cast<size_t>(slice_size) * sizeof(float); // NOLINT
|
|
int64_t c = 0;
|
|
for (int64_t i = 0; i < index_tensor.numel(); i++) {
|
|
std::memcpy(buffer_data + c * slice_size,
|
|
src_data + index_data[i] * slice_size,
|
|
copy_bytes);
|
|
c += 1;
|
|
}
|
|
};
|
|
index_select(src_tensor, index_tensor, buffer_tensor);
|
|
|
|
// Copy the data to device memory
|
|
cudaMemcpyAsync(dst_data + (numel * size),
|
|
buffer_tensor->data<float>(),
|
|
index_tensor.numel() * size * sizeof(float),
|
|
cudaMemcpyHostToDevice,
|
|
stream);
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_async_write(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
auto& src = GetTensorFromArgs("async_write", "src", args, 0, false);
|
|
auto& dst = GetTensorFromArgs("async_write", "dst", args, 1, false);
|
|
auto& offset = GetTensorFromArgs("async_write", "offset", args, 2, false);
|
|
auto& count = GetTensorFromArgs("async_write", "count", args, 3, false);
|
|
const phi::distributed::ProcessMesh* mesh = nullptr;
|
|
if (InputsContainDistTensor(&mesh, src, dst, offset, count)) {
|
|
ConvertAllInputsToDistTensor(mesh, src, dst, offset, count);
|
|
}
|
|
{
|
|
eager_gil_scoped_release guard;
|
|
EagerSetDeviceId();
|
|
PADDLE_ENFORCE_EQ(
|
|
src.is_gpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `src` device should be CUDAPlace, but received %d. ",
|
|
src.place()));
|
|
PADDLE_ENFORCE_EQ(dst.is_gpu_pinned(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `dst` device should be CUDAPinnedPlace, "
|
|
"but received %d. ",
|
|
dst.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
offset.is_cpu(),
|
|
true,
|
|
common::errors::InvalidArgument("Required `offset` device should "
|
|
"be CPUPlace, but received %d. ",
|
|
offset.place()));
|
|
PADDLE_ENFORCE_EQ(
|
|
count.is_cpu(),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"Required `count` device should be CPUPlace, but received %d. ",
|
|
count.place()));
|
|
|
|
// TODO(daisiming): In future, add index as arguments following
|
|
// async_read.
|
|
auto& src_tensor = src;
|
|
auto* dst_tensor = &dst;
|
|
auto& offset_tensor = offset;
|
|
auto& count_tensor = count;
|
|
const auto& deviceId = paddle::platform::GetCurrentDeviceId();
|
|
|
|
PADDLE_ENFORCE_EQ(offset_tensor.dims().size(),
|
|
1,
|
|
common::errors::InvalidArgument(
|
|
"`offset` tensor should be one-dimensional."));
|
|
PADDLE_ENFORCE_EQ(count_tensor.dims().size(),
|
|
1,
|
|
common::errors::InvalidArgument(
|
|
"`count` tensor should be one-dimensional."));
|
|
PADDLE_ENFORCE_EQ(offset_tensor.numel(),
|
|
count_tensor.numel(),
|
|
common::errors::InvalidArgument(
|
|
"`offset` and `count` tensor size mismatch."));
|
|
PADDLE_ENFORCE_EQ(src_tensor.dims().size(),
|
|
dst_tensor->dims().size(),
|
|
common::errors::InvalidArgument(
|
|
"`src` and `dst` should have the same tensor shape, "
|
|
"except for the first dimension."));
|
|
for (int i = 1; i < src_tensor.dims().size(); i++) {
|
|
PADDLE_ENFORCE_EQ(
|
|
src_tensor.dims()[i],
|
|
dst_tensor->dims()[i],
|
|
common::errors::InvalidArgument(
|
|
"`src` and `dst` should have the same tensor shape, "
|
|
"except for the first dimension."));
|
|
}
|
|
|
|
auto stream = paddle::platform::get_current_stream(deviceId)->raw_stream();
|
|
|
|
int64_t size = src_tensor.numel() / src_tensor.dims()[0];
|
|
auto* src_data = src_tensor.data<float>();
|
|
auto* dst_data = dst_tensor->data<float>();
|
|
const int64_t* offset_data = offset_tensor.data<int64_t>();
|
|
const int64_t* count_data = count_tensor.data<int64_t>();
|
|
int64_t src_offset = 0, dst_offset, c;
|
|
for (int64_t i = 0; i < offset_tensor.numel(); i++) {
|
|
dst_offset = offset_data[i], c = count_data[i];
|
|
PADDLE_ENFORCE_LE(
|
|
src_offset + c,
|
|
src_tensor.dims()[0],
|
|
common::errors::InvalidArgument("Invalid offset or count index"));
|
|
PADDLE_ENFORCE_LE(
|
|
dst_offset + c,
|
|
dst_tensor->dims()[0],
|
|
common::errors::InvalidArgument("Invalid offset or count index"));
|
|
cudaMemcpyAsync(dst_data + (dst_offset * size),
|
|
src_data + (src_offset * size),
|
|
c * size * sizeof(float),
|
|
cudaMemcpyDeviceToHost,
|
|
stream);
|
|
src_offset += c;
|
|
}
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_to_uva_tensor(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
VLOG(4) << "Running in eager_api_to_uva_tensor.";
|
|
auto new_tensor = std::make_shared<Tensor>(
|
|
egr::Controller::Instance().GenerateUniqueName());
|
|
PyObject* obj = PyTuple_GET_ITEM(args, 0);
|
|
auto array = py::cast<py::array>(py::handle(obj));
|
|
|
|
Py_ssize_t args_num = PyTuple_Size(args);
|
|
int64_t device_id = 0;
|
|
if (args_num > 1) {
|
|
PyObject* Py_device_id = PyTuple_GET_ITEM(args, 1);
|
|
if (Py_device_id) {
|
|
device_id = CastPyArg2AttrLong(Py_device_id, 1);
|
|
}
|
|
}
|
|
|
|
if (py::isinstance<py::array_t<int32_t>>(array)) {
|
|
SetUVATensorFromPyArray<int32_t>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<int64_t>>(array)) {
|
|
SetUVATensorFromPyArray<int64_t>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<float>>(array)) {
|
|
SetUVATensorFromPyArray<float>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<double>>(array)) {
|
|
SetUVATensorFromPyArray<double>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<int8_t>>(array)) {
|
|
SetUVATensorFromPyArray<int8_t>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<int16_t>>(array)) {
|
|
SetUVATensorFromPyArray<int16_t>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<phi::float16>>(array)) {
|
|
SetUVATensorFromPyArray<phi::float16>(new_tensor, array, device_id);
|
|
} else if (py::isinstance<py::array_t<bool>>(array)) {
|
|
SetUVATensorFromPyArray<bool>(new_tensor, array, device_id);
|
|
} else {
|
|
// obj may be any type, obj.cast<py::array>() may be failed,
|
|
// then the array.dtype will be string of unknown meaning.
|
|
PADDLE_THROW(common::errors::InvalidArgument(
|
|
"Input object type error or incompatible array data type. "
|
|
"tensor.set() supports array with bool, float16, float32, "
|
|
"float64, int8, int16, int32, int64,"
|
|
"please check your input or input array data type."));
|
|
}
|
|
return ToPyObject(*(new_tensor.get()));
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
#endif
|
|
|
|
static PyObject* eager_api__add_backward_final_hook(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
PyObject* hook_func = PyTuple_GET_ITEM(args, 0);
|
|
egr::Controller::Instance().RegisterBackwardFinalHook(
|
|
std::make_shared<PyVoidHook>(hook_func));
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
static PyObject* eager_api_set_master_grads(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
// tensor_list is a list of model parameters.
|
|
auto tensor_list = CastPyArg2VectorOfTensor(PyTuple_GET_ITEM(args, 0), 0);
|
|
for (auto& tensor : tensor_list) {
|
|
VLOG(6) << "set master_grad for tensor: " << tensor.name();
|
|
if (!egr::EagerUtils::IsLeafTensor(tensor)) {
|
|
continue;
|
|
}
|
|
Tensor* grad = egr::EagerUtils::mutable_grad(tensor);
|
|
PADDLE_ENFORCE_NE(
|
|
grad,
|
|
nullptr,
|
|
common::errors::Fatal("Detected nullptr grad. "
|
|
"Please check if you have manually cleared "
|
|
"the grad inside autograd_meta"));
|
|
if (((*grad).has_allocation() || (*grad).is_dist_tensor()) &&
|
|
((*grad).dtype() == DataType::FLOAT16 ||
|
|
(*grad).dtype() == DataType::BFLOAT16)) {
|
|
auto master_grad = paddle::experimental::cast(*grad, DataType::FLOAT32);
|
|
grad->set_impl(master_grad.impl());
|
|
}
|
|
VLOG(6) << "finish setting master_grad for tensor: " << tensor.name();
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager__is_run_in_backward(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
|
|
return ToPyObject(egr::Controller::Instance().GetIsInBackward());
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
PyObject* eager__add_doc_str(PyObject* self, PyObject* args) {
|
|
EAGER_TRY
|
|
static std::vector<std::string> all_docs;
|
|
PyObject* func_obj = nullptr;
|
|
PyObject* doc_obj = nullptr;
|
|
PyObject* sig_obj = nullptr;
|
|
PyObject* annotation_obj = nullptr;
|
|
if (!PyArg_ParseTuple(
|
|
args, "OOOO", &func_obj, &doc_obj, &sig_obj, &annotation_obj)) {
|
|
return nullptr;
|
|
}
|
|
if (PyDict_Check(annotation_obj) == false) {
|
|
PADDLE_THROW(common::errors::InvalidArgument(
|
|
"The 4th arg which be used to init __annotations__ must be dict in "
|
|
"python!"));
|
|
return nullptr;
|
|
}
|
|
std::string doc_string = CastPyArg2AttrString(doc_obj, 1);
|
|
|
|
if (Py_TYPE(func_obj) == &PyCFunction_Type) {
|
|
PyCFunctionObject* f = reinterpret_cast<PyCFunctionObject*>(func_obj);
|
|
if (f->m_ml->ml_doc) {
|
|
VLOG(6)
|
|
<< "eager__add_doc_str will update doc for PyCFunction, original doc "
|
|
<< f->m_ml->ml_doc;
|
|
}
|
|
all_docs.emplace_back(doc_string);
|
|
f->m_ml->ml_doc = all_docs.back().c_str();
|
|
if (func_obj->ob_type->tp_dict == nullptr) {
|
|
func_obj->ob_type->tp_dict = PyDict_New();
|
|
}
|
|
// if (PyDict_SetItemString(
|
|
// func_obj->ob_type->tp_dict, "__text_signature__", sig_obj) < 0) {
|
|
// VLOG(6) << "eager__add_doc_str add __text_signature__ failed";
|
|
// return nullptr;
|
|
// }
|
|
// Py_INCREF(sig_obj);
|
|
if (PyDict_SetItemString(func_obj->ob_type->tp_dict,
|
|
"__annotations__",
|
|
annotation_obj) < 0) {
|
|
VLOG(6) << "eager__add_doc_str add __annotations__ failed";
|
|
return nullptr;
|
|
}
|
|
Py_INCREF(annotation_obj);
|
|
}
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager__for_test_check_cuda_error(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
#ifdef PADDLE_WITH_CUDA
|
|
// 1. wait all kernel finish
|
|
PADDLE_ENFORCE_GPU_SUCCESS(cudaDeviceSynchronize());
|
|
|
|
// 2. get error state
|
|
PADDLE_ENFORCE_GPU_SUCCESS(cudaGetLastError());
|
|
|
|
// 3. check if cuda 700
|
|
size_t bytes = 256;
|
|
char* cuda_mem;
|
|
char* cpu_mem = new char[bytes + 1];
|
|
|
|
cudaMalloc(&cuda_mem, bytes + 1);
|
|
cudaMemset(cuda_mem, 0, bytes + 1);
|
|
cudaMemcpyAsync(cpu_mem, cuda_mem, bytes, cudaMemcpyDeviceToHost);
|
|
|
|
cudaFree(cuda_mem);
|
|
delete[] cpu_mem;
|
|
#endif
|
|
RETURN_PY_NONE
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
PyObject* eager__start_capture_backward_viz_subgraph(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
std::string dump_dir_path =
|
|
CastPyArg2AttrString(PyTuple_GET_ITEM(args, 0), 0);
|
|
bool need_dump_grad_tensors =
|
|
CastPyArg2AttrBoolean(PyTuple_GET_ITEM(args, 1), 1);
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance().SetDumpDirPath(
|
|
dump_dir_path);
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance().SetNeedDumpGradTensors(
|
|
need_dump_grad_tensors);
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance()
|
|
.StartCaptureSubGraphForViz();
|
|
RETURN_PY_NONE
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager__stop_capture_backward_viz_subgraph(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance()
|
|
.StopCaptureSubGraphForViz();
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
PyObject* eager__stop_capture_backward_vlog_subgraph(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance()
|
|
.StopCaptureSubGraphForVlog();
|
|
RETURN_PY_NONE
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyObject* eager__start_capture_backward_vlog_subgraph(PyObject* self,
|
|
PyObject* args,
|
|
PyObject* kwargs) {
|
|
EAGER_TRY
|
|
|
|
int subgraph_vlog_level = CastPyArg2AttrInt(PyTuple_GET_ITEM(args, 0), 0);
|
|
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance().SetSubGraphBwdVlogLevel(
|
|
subgraph_vlog_level);
|
|
egr::EagerBackwardSubGraphNodeRecorder::Instance()
|
|
.StartCaptureSubGraphForVlog();
|
|
RETURN_PY_NONE
|
|
|
|
EAGER_CATCH_AND_THROW_RETURN_NULL
|
|
}
|
|
|
|
PyMethodDef variable_functions[] = { // NOLINT
|
|
// TODO(jiabin): Remove scale when we have final state tests
|
|
{"scale",
|
|
(PyCFunction)(void (*)())eager_api_scale,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_add_backward_final_hook",
|
|
(PyCFunction)(void (*)())eager_api__add_backward_final_hook,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"run_backward",
|
|
(PyCFunction)(void (*)())eager_api_run_backward,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"run_partial_grad",
|
|
(PyCFunction)(void (*)())eager_api_run_partial_grad,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_get_custom_operator_inplace_map",
|
|
(PyCFunction)(void (*)())
|
|
eager_api__get_custom_operator_inplace_reverse_idx,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_run_custom_op",
|
|
(PyCFunction)(void (*)())eager_api_run_custom_op,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"tensor_copy",
|
|
(PyCFunction)(void (*)())eager_api_tensor_copy,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"get_all_grads",
|
|
(PyCFunction)(void (*)())eager_api_get_all_grads,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"get_grads_lists",
|
|
(PyCFunction)(void (*)())eager_api_get_grads_lists,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"get_grads_types",
|
|
(PyCFunction)(void (*)())eager_api_get_grads_types,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"read_next_tensor_list",
|
|
(PyCFunction)(void (*)())eager_api_read_next_tensor_list,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"jit_function_call",
|
|
(PyCFunction)(void (*)())eager_api_jit_function_call,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
/**sparse functions**/
|
|
{"sparse_coo_tensor",
|
|
(PyCFunction)(void (*)())eager_api_sparse_coo_tensor,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"sparse_csr_tensor",
|
|
(PyCFunction)(void (*)())eager_api_sparse_csr_tensor,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"register_saved_tensors_hooks",
|
|
(PyCFunction)(void (*)())eager_api_register_saved_tensors_hooks,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"reset_saved_tensors_hooks",
|
|
(PyCFunction)(void (*)())eager_api_reset_saved_tensors_hooks,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
/**amp functions**/
|
|
{"set_master_grads",
|
|
(PyCFunction)(void (*)())eager_api_set_master_grads,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_is_run_in_backward",
|
|
(PyCFunction)(void (*)())eager__is_run_in_backward,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_for_test_check_cuda_error",
|
|
(PyCFunction)(void (*)())eager__for_test_check_cuda_error,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
|
|
{"_add_docstr",
|
|
(PyCFunction)(void (*)())eager__add_doc_str,
|
|
METH_VARARGS,
|
|
nullptr},
|
|
{"_start_capture_backward_viz_subgraph",
|
|
(PyCFunction)(void (*)())eager__start_capture_backward_viz_subgraph,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_stop_capture_backward_viz_subgraph",
|
|
(PyCFunction)(void (*)())eager__stop_capture_backward_viz_subgraph,
|
|
METH_VARARGS,
|
|
nullptr},
|
|
{"_start_capture_backward_vlog_subgraph",
|
|
(PyCFunction)(void (*)())eager__start_capture_backward_vlog_subgraph,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_stop_capture_backward_vlog_subgraph",
|
|
(PyCFunction)(void (*)())eager__stop_capture_backward_vlog_subgraph,
|
|
METH_VARARGS,
|
|
nullptr},
|
|
/**sparse functions**/
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
{"async_read",
|
|
(PyCFunction)(void (*)())eager_api_async_read,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"async_write",
|
|
(PyCFunction)(void (*)())eager_api_async_write,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"to_uva_tensor",
|
|
(PyCFunction)(void (*)())eager_api_to_uva_tensor,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
{"_print_tensor_in_gpu",
|
|
(PyCFunction)(void (*)())eager_api_print_tensor_in_gpu,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
nullptr},
|
|
#endif
|
|
{nullptr, nullptr, 0, nullptr}};
|
|
|
|
void BindFunctions(PyObject* module) {
|
|
if (PyModule_AddFunctions(module, variable_functions) < 0) {
|
|
PADDLE_THROW(common::errors::Fatal(
|
|
"Init Paddle error in BindFunctions(PyModule_AddFunctions)."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
} // namespace paddle::pybind
|