chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+781
View File
@@ -0,0 +1,781 @@
# 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.
import argparse
import re
import yaml
from api_base import PREFIX_TENSOR_NAME, BaseAPI, IsUsePredefinedOut
backward_api_black_list = [
"scale_grad", # tensor = scale is not implemented in api_custom_impl.cc
]
inplace_out_type_map = {
"Tensor": "Tensor&",
"std::vector<Tensor>": "std::vector<Tensor>&",
}
inplace_optional_out_type_map = {
"Tensor": "paddle::optional<Tensor>&",
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>&",
}
optional_out_type_map = {
"Tensor": "paddle::optional<Tensor>",
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>",
}
class ForwardAPI(BaseAPI):
def __init__(self, api_item_yaml):
super().__init__(api_item_yaml)
self.is_dygraph_api, self.intermediate_outs = self.parse_intermediate(
api_item_yaml
)
self.inplace_map, self.view_map = self.parse_inplace_and_view(
api_item_yaml
)
def get_api_func_name(self):
if self.is_dygraph_api:
return self.api + '_intermediate'
else:
return self.api
def gene_input(self, kernel_tensor_type=None, code_indent=''):
kernel_param = self.kernel['param']
input_name_tensor_map, input_tensor_code = super().gene_input(
kernel_tensor_type, code_indent
)
# generate the input that is in view list
for i, input_name in enumerate(self.inputs['names']):
if (
input_name in self.view_map.values()
and input_name not in input_name_tensor_map.keys()
):
if (
kernel_tensor_type is None
or kernel_tensor_type[0][kernel_param.index(input_name)]
== 'dense'
):
trans_flag = self.gene_trans_flag(input_name)
input_tensor_code = (
input_tensor_code
+ f"""
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = PrepareData({input_name}, kernel.InputAt(0), {trans_flag}, kernel_result.is_stride_kernel);"""
)
else:
# do nothing
pass
return input_name_tensor_map, input_tensor_code
def parse_intermediate(self, api_item_yaml):
if 'intermediate' in api_item_yaml:
intermediate_outs = [
item.strip()
for item in api_item_yaml['intermediate'].split(',')
]
return True, intermediate_outs
else:
return False, []
def parse_inplace_and_view(self, api_item_yaml):
inplace_map, view_map = {}, {}
for mode in ['inplace', 'view']:
if mode in api_item_yaml:
if mode == 'inplace':
inplace_map = {}
else:
view_map = {}
in_out_mapping_list = api_item_yaml[mode].split(',')
for item in in_out_mapping_list:
result = re.search(r"(?P<in>\w+)\s*->\s*(?P<out>\w+)", item)
in_val = result.group('in')
out_val = result.group('out')
assert in_val in self.inputs['names'], (
f"{self.api} : {mode} input error: the input var name('{in_val}') is not found in the input args of {self.api}."
)
assert out_val in self.outputs['names'], (
f"{self.api} : {mode} output error: the output var name('{out_val}') is not found in the output args of {self.api}."
)
if mode == 'inplace':
inplace_map[out_val] = in_val
else:
view_map[out_val] = in_val
return inplace_map, view_map
def get_return_type_with_intermediate(self, inplace_flag=False):
out_type_list = []
for i, out_type in enumerate(self.outputs['types']):
out_name = self.outputs['names'][i].split('@')[0]
if inplace_flag and out_name in self.inplace_map:
if self.inplace_map[out_name] in self.optional_vars:
out_type_list.append(
inplace_optional_out_type_map[out_type]
)
else:
out_type_list.append(inplace_out_type_map[out_type])
else:
out_type_list.append(out_type)
if len(out_type_list) == 1:
return out_type_list[0]
else:
return "std::tuple<" + ", ".join(out_type_list) + ">"
def get_return_type(self, inplace_flag=False):
out_type_list = []
for i, out_type in enumerate(self.outputs['types']):
out_name = self.outputs['names'][i].split('@')[0]
if inplace_flag and out_name in self.inplace_map:
if self.inplace_map[out_name] in self.optional_vars:
out_type_list.append(
inplace_optional_out_type_map[out_type]
)
else:
out_type_list.append(inplace_out_type_map[out_type])
elif self.is_dygraph_api or out_name not in self.intermediate_outs:
out_type_list.append(out_type)
if len(out_type_list) == 1:
return out_type_list[0]
else:
return "std::tuple<" + ", ".join(out_type_list) + ">"
def gene_return_code(self):
if self.is_dygraph_api or len(self.intermediate_outs) == 0:
return "return api_output;"
else:
return_out_list = []
for i, name in enumerate(self.outputs['names']):
if name.split('@')[0] not in self.intermediate_outs:
return_out_list.append(i)
if len(return_out_list) == 1:
return f"return std::get<{return_out_list[0]}>(api_output);"
else:
selected_code = [
f"std::get<{i}>(api_output)" for i in return_out_list
]
return 'return std::make_tuple(' + ", ".join(selected_code) + ');'
def gene_fallback_code_after_gene_output_of_vector(
self, code_indent, output_idx, is_inplace, is_optional
):
fallback_code = ""
if is_inplace and is_optional:
fallback_code = f"""
{code_indent} if (kernel_result.has_fallback_cpu) {{
{code_indent} for (size_t i = 0; i < kernel_out_{output_idx}.size(); ++i) {{
{code_indent} kernel_out_{output_idx}[i] = const_cast<phi::DenseTensor*>({PREFIX_TENSOR_NAME}{self.inplace_map[self.outputs['names'][output_idx]]}->at(i));
{code_indent} }}
{code_indent} }}"""
elif is_inplace:
fallback_code = f"""
{code_indent} if (kernel_result.has_fallback_cpu) {{
{code_indent} for (size_t i = 0; i < kernel_out_{output_idx}.size(); ++i) {{
{code_indent} kernel_out_{output_idx}[i] = const_cast<phi::DenseTensor*>({PREFIX_TENSOR_NAME}{self.inplace_map[self.outputs['names'][output_idx]]}[i]);
{code_indent} }}
{code_indent} }}"""
else:
fallback_code = ""
return fallback_code
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
kernel_output = []
output_names = []
output_create = ""
return_type = self.get_return_type_with_intermediate(inplace_flag)
if len(out_dtype_list) == 1:
kernel_output.append('kernel_out')
output_names.append('kernel_out')
inplace_assign = (
" = " + self.inplace_map[self.outputs['names'][0]]
if inplace_flag and self.outputs['names'][0] in self.inplace_map
else ""
)
if (
len(self.outputs['names']) == 1
and self.outputs['types'][0] == "Tensor"
and not (
inplace_flag
and self.outputs['names'][0].split('@')[0]
in self.inplace_map
)
and self.api != "empty_like"
):
output_create = f"""
{code_indent} Tensor out_tmp; Tensor& api_output = predefined_out ? **predefined_out : out_tmp;"""
else:
output_create = f"""
{code_indent} {return_type} api_output{inplace_assign};"""
set_out_func = (
'SetKernelOutput'
if out_tensor_type_list is None
or out_tensor_type_list[0] == 'dense'
else 'SetSelectedRowsKernelOutput'
)
if (
return_type == 'std::vector<Tensor>'
or return_type == 'std::vector<Tensor>&'
):
assert self.outputs['out_size_expr'][0] is not None, (
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
)
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}({self.outputs['out_size_expr'][0]}, &api_output);"""
)
elif (
return_type == 'paddle::optional<std::vector<Tensor>>'
or return_type == 'paddle::optional<std::vector<Tensor>>&'
):
assert self.outputs['out_size_expr'][0] is not None, (
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
)
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}({self.outputs['out_size_expr'][0]}, api_output.get_ptr());"""
)
elif (
return_type == 'paddle::optional<Tensor>'
or return_type == 'paddle::optional<Tensor>&'
):
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}(api_output.get_ptr());"""
)
elif return_type == 'Tensor' or return_type == 'Tensor&':
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}(&api_output);"""
)
if (
not inplace_flag
and self.view_map is not None
and self.outputs['names'][0] in self.view_map
):
output_create = (
output_create
+ f"""
{code_indent} kernel_out->ShareBufferWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][0]]});
{code_indent} kernel_out->ShareInplaceVersionCounterWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][0]]});
{code_indent} VLOG(5) << "Perform View between Output and Input Tensor, share allocation and inplace version.";"""
)
elif len(out_dtype_list) > 1:
if not (
inplace_flag
and any(
name.split('@')[0] in self.inplace_map
for name in self.outputs['names']
)
):
if IsUsePredefinedOut(self.outputs['types']):
length = len(self.outputs['names'])
if length == 1:
output_create = f"""
{code_indent} Tensor out_tmp; Tensor& api_output = predefined_out ? **predefined_out : out_tmp;"""
else:
tuple_types = ", ".join(["Tensor"] * length)
get_indices = ", ".join(
f"*std::get<{i}>(*predefined_out)"
for i in range(length)
)
output_create = f"""
{code_indent} std::tuple<{tuple_types}> out_tmp;
{code_indent} paddle::optional<std::tuple<{tuple_types}>> predefined_out_value;
{code_indent} if(predefined_out) {{ predefined_out_value = std::make_tuple({get_indices}); }}
{code_indent} std::tuple<{tuple_types}>& api_output = predefined_out_value ? *predefined_out_value : out_tmp;"""
else:
output_create = f"""
{code_indent} {return_type} api_output;"""
else:
output_create = f"""
{code_indent} {return_type} api_output;"""
if inplace_flag:
output_create = f"""
{code_indent} {return_type} api_output{{"""
for out_name in self.outputs['names']:
if out_name in self.inplace_map:
output_create += self.inplace_map[out_name] + ', '
else:
output_create += 'Tensor(), '
output_create = output_create[:-2] + '};'
for i in range(len(out_dtype_list)):
kernel_output.append(f'kernel_out_{i}')
output_names.append(f'kernel_out_{i}')
set_out_func = (
'SetKernelOutput'
if out_tensor_type_list is None
or out_tensor_type_list[i] == 'dense'
else 'SetSelectedRowsKernelOutput'
)
get_out_code = f"&std::get<{i}>(api_output)"
if (
inplace_flag
and self.outputs['names'][i] in self.inplace_map
and self.inplace_map[self.outputs['names'][i]]
in self.optional_vars
):
get_out_code = f"std::get<{i}>(api_output).get_ptr()"
if out_dtype_list[i] == 'std::vector<Tensor>':
assert self.outputs['out_size_expr'][i] is not None, (
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
)
# Special case for inplace vector and inplace optional<vector>
if self.outputs['names'][i] in self.inplace_map:
set_out_func = "SetInplaceVectorKernelOutput"
if (
self.inplace_map[self.outputs['names'][i]]
in self.optional_vars
):
set_out_func = (
"SetInplaceOptionalVectorKernelOutput"
)
get_out_code = f"std::get<{i}>(api_output)"
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
+ self.gene_fallback_code_after_gene_output_of_vector(
code_indent, i, True, True
)
)
else:
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
+ self.gene_fallback_code_after_gene_output_of_vector(
code_indent, i, True, False
)
)
else:
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['out_size_expr'][i]}, {get_out_code});"""
)
else:
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}({get_out_code});"""
)
if (
not inplace_flag
and self.view_map is not None
and self.outputs['names'][i] in self.view_map
):
if out_dtype_list[i] == 'Tensor':
output_create = (
output_create
+ f"""
{code_indent} kernel_out_{i}->ShareBufferWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][i]]});
{code_indent} kernel_out_{i}->ShareInplaceVersionCounterWith(*{PREFIX_TENSOR_NAME}{self.view_map[self.outputs['names'][i]]});
{code_indent} VLOG(5) << "Perform View between Output and Input Tensor, share allocation and inplace version.";"""
)
else:
raise ValueError(
f"{self.api} : Output error: only support Tensor type when use view in yaml. But get {out_dtype_list[i]}"
)
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return kernel_output, output_names, output_create
def reset_view_after_fallback(
self, out_dtype_list, code_indent='', inplace_flag=False
):
remap_code = ''
if len(out_dtype_list) == 1:
if (
not inplace_flag
and self.view_map is not None
and self.outputs['names'][0] in self.view_map
):
remap_code += f"""
{code_indent} phi::DenseTensor * {self.view_map[self.outputs['names'][0]]}_remap = static_cast<phi::DenseTensor*>({self.view_map[self.outputs['names'][0]]}.impl().get());
{code_indent} {self.view_map[self.outputs['names'][0]]}_remap->ShareBufferWith(*kernel_out);
{code_indent} kernel_out->ShareInplaceVersionCounterWith(*{self.view_map[self.outputs['names'][0]]}_remap);
"""
elif len(out_dtype_list) > 1:
for i in range(len(out_dtype_list)):
if (
not inplace_flag
and self.view_map is not None
and self.outputs['names'][i] in self.view_map
):
remap_code += f"""
{code_indent} phi::DenseTensor * {self.view_map[self.outputs['names'][i]]}_remap = static_cast<phi::DenseTensor*>({self.view_map[self.outputs['names'][i]]}.impl().get());
{code_indent} {self.view_map[self.outputs['names'][i]]}_remap->ShareBufferWith(*kernel_out_{i});
{code_indent} kernel_out_{i}->ShareInplaceVersionCounterWith(*{self.view_map[self.outputs['names'][i]]}_remap);
"""
return remap_code
class BackwardAPI(ForwardAPI):
def gene_base_api_code(
self, inplace_flag=False, grad_flag=False, append_predefined_out=True
):
api_func_name = self.get_api_func_name()
if inplace_flag and api_func_name[-1] != '_':
inplace_name = api_func_name + '_'
else:
inplace_name = api_func_name
api_code = f"""
PADDLE_API {self.get_return_type(inplace_flag)} {inplace_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=append_predefined_out)}) {{
{self.get_grad_outputs_define(inplace_flag)}
{self.get_optional_inputs_change(inplace_flag)}
{api_func_name}({self.get_grad_api_call_args(inplace_flag)});
return {self.get_grad_output(inplace_flag)};
}}
"""
return api_code
def gene_api_code(self, grad_flag=False, append_predefined_out=False):
if not self.is_base_api and not self.is_only_composite_api:
invoke_func_name = self.invoke.split('(')[0]
if (not invoke_func_name.endswith("_grad")) and (
not invoke_func_name.endswith('_impl')
):
return ""
if self.is_only_composite_api:
return ""
api_code = self.gene_base_api_code(
grad_flag=grad_flag, append_predefined_out=append_predefined_out
)
if self.is_base_api and len(self.inplace_map) > 0:
if self.api[-1] == '_':
api_code = ""
api_code = api_code + self.gene_base_api_code_for_inplace()
return api_code
def gene_api_declaration(self, grad_flag=False, append_predefined_out=True):
if not self.is_base_api and not self.is_only_composite_api:
invoke_func_name = self.invoke.split('(')[0]
if (not invoke_func_name.endswith("_grad")) and (
not invoke_func_name.endswith('_impl')
):
return ""
if self.is_only_composite_api:
return ""
api_declaration = ""
api_func_name = self.get_api_func_name()
if api_func_name[-1] != '_':
api_declaration = f"""
PADDLE_API {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=append_predefined_out)});
"""
if self.is_base_api and len(self.inplace_map) > 0:
if api_func_name[-1] != '_':
api_func_name += '_'
api_declaration = (
api_declaration
+ f"""
PADDLE_API {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=append_predefined_out)});
"""
)
return api_declaration
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path):
return f"""
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
{header_file_path}
#include "paddle/phi/api/lib/api_custom_impl.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/api_registry.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/include/tensor_utils.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/common/type_traits.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/multiary.h"
#include "paddle/phi/infermeta/nullary.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/ternary.h"
#include "paddle/phi/infermeta/fusion.h"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/nccl_comm_context.h"
#elif (defined(PADDLE_WITH_XPU) && defined(PADDLE_WITH_XPU_BKCL))
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
#elif PADDLE_WITH_CUSTOM_DEVICE
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/xccl_comm_context.h"
#endif
#ifdef PADDLE_WITH_DISTRIBUTE
#include "paddle/phi/core/distributed/store/store_utils.h"
#include "paddle/phi/infermeta/spmd_rules/rules.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#endif
PD_DECLARE_bool(conv2d_disable_cudnn);
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def api_namespace():
return (
"""
namespace paddle {
namespace experimental {
""",
"""
} // namespace experimental
} // namespace paddle
""",
)
def declare_extension_api():
return """
namespace paddle {
PD_DECLARE_API(from_blob);
#ifdef PADDLE_WITH_DISTRIBUTE
PD_DECLARE_API(reshard);
#endif
} // namespace paddle
"""
def generate_api(
api_yaml_path,
is_fused_ops_yaml,
header_file_path,
source_file_path,
grad_flag,
):
apis = []
for each_api_yaml in api_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
apis.extend(api_list)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
if not grad_flag:
include_header_file = (
'#include "paddle/phi/api/include/fused_api.h"'
if is_fused_ops_yaml is True
else '#include "paddle/phi/api/include/api.h"'
)
else:
include_header_file = (
'#include "paddle/phi/api/backward/fused_backward_api.h" \n'
'#include "paddle/phi/api/backward/fused_backward_api_base.h" '
if is_fused_ops_yaml is True
else '#include "paddle/phi/api/backward/backward_api.h" \n'
'#include "paddle/phi/api/backward/backward_api_base.h" '
)
# not all fused ops support dygraph
if is_fused_ops_yaml is True:
new_apis = [
api
for api in apis
if "support_dygraph_mode" in api
and api["support_dygraph_mode"] is True
]
apis = new_apis
source_file.write(source_include(include_header_file))
source_file.write(namespace[0])
for api in apis:
if not grad_flag:
forward_api = ForwardAPI(api)
else:
forward_api = BackwardAPI(api)
if forward_api.api in backward_api_black_list:
continue
if forward_api.is_dygraph_api and not is_fused_ops_yaml:
forward_api.is_dygraph_api = False
if forward_api.is_dygraph_api and is_fused_ops_yaml:
forward_api.is_dygraph_api = False
header_file.write(
forward_api.gene_api_declaration(
grad_flag=grad_flag, append_predefined_out=not grad_flag
)
)
source_file.write(forward_api.gene_api_code(grad_flag=grad_flag))
forward_api.is_dygraph_api = True
header_file.write(
forward_api.gene_api_declaration(
grad_flag=grad_flag, append_predefined_out=not grad_flag
)
)
source_file.write(forward_api.gene_api_code(grad_flag=grad_flag))
header_file.write(namespace[1])
source_file.write(namespace[1])
source_file.write(declare_extension_api())
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to api yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/ops.yaml'],
)
parser.add_argument(
'--backward_api_yaml_path',
help='path to api yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/backward.yaml'],
)
parser.add_argument(
'--is_fused_ops_yaml',
help='flag of fused ops yaml',
action='store_true',
)
parser.add_argument(
'--api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/include/api.h',
)
parser.add_argument(
'--api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/api.cc',
)
parser.add_argument(
'--backward_api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/backward/backward_api.h',
)
parser.add_argument(
'--backward_api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/backward_api.cc',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
backward_api_yaml_path = options.backward_api_yaml_path
is_fused_ops_yaml = options.is_fused_ops_yaml
header_file_path = options.api_header_path
source_file_path = options.api_source_path
backward_header_file_path = options.backward_api_header_path
backward_source_file_path = options.backward_api_source_path
generate_api(
api_yaml_path,
is_fused_ops_yaml,
header_file_path,
source_file_path,
False,
)
generate_api(
backward_api_yaml_path,
is_fused_ops_yaml,
backward_header_file_path,
backward_source_file_path,
True,
)
if __name__ == '__main__':
main()
@@ -0,0 +1,430 @@
# 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.
import argparse
import re
import yaml
from api_base import BaseAPI
class BackwardAPI(BaseAPI):
def __init__(self, backward_item_yaml):
super().__init__(backward_item_yaml)
self.check_args(backward_item_yaml['forward'])
self.no_need_buffer = self.parse_no_need_buffer(backward_item_yaml)
def get_api_name(self, api_item_yaml):
return api_item_yaml['backward_op']
def parse_forward_config(self, forward_config):
# api_name (const Tensor& input, ... , int attr, ...) -> Tensor(out)
result = re.search(
r"(?P<op>[a-z][a-z0-9_]+)\s*(?P<args>\([^\)]+\))\s*->\s*(?P<outputs>.+)",
forward_config,
)
api = result.group('op')
(
_,
outputs,
_,
) = self.parse_output(self.api, result.group('outputs'))
outputs = [item.split('@')[0] for item in outputs]
fw_inputs, fw_attrs = self.parse_input_and_attr(
api, result.group('args')
)
return api, fw_inputs, fw_attrs, outputs
def parse_no_need_buffer(self, api_item_yaml):
no_need_buffer = []
if 'no_need_buffer' in api_item_yaml:
no_need_buffer = [
item.strip()
for item in api_item_yaml['no_need_buffer'].split(',')
]
return no_need_buffer
def check_args(self, forward_config):
# parse the forward and backward config
_, fw_inputs, fw_attrs, fw_outputs = self.parse_forward_config(
forward_config
)
# check the inputs of backward
for input in self.inputs['names']:
if input not in fw_inputs['names'] and input not in fw_outputs:
if input.endswith('_grad'):
original_name = input[:-5]
assert original_name in fw_outputs, (
f"{self.api} : Input Tensor error: the input tensor({input}) of backward should be an input or output or grad of output in forward api. \
Please check the forward of {self.api} in yaml."
)
# check the attributes of backward
for attr in self.attrs['names']:
assert (
attr in fw_attrs['names']
and self.attrs['attr_info'][attr][0]
== fw_attrs['attr_info'][attr][0]
) or self.attrs['attr_info'][attr][1] is not None, (
f"{self.api} : Attribute error: The attribute({attr}) of backward isn't consistent with forward api or doesn't have default value. \
Please check the args of {self.api} in yaml."
)
# check the output of backward
assert len(self.outputs['types']) <= len(fw_inputs['names']), (
f"{self.api} : Output error: The number of outputs should be less then the number of inputs of forward api. \
Please check the output of {self.api} in yaml."
)
def get_declare_args(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
return self.get_define_args(
grad_flag=grad_flag, append_predefined_out=append_predefined_out
)
def get_define_args(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
out_type_map = {
'Tensor': 'Tensor*',
'std::vector<Tensor>': 'std::vector<Tensor*>',
}
inputs_and_attrs = super().get_define_args(
grad_flag=grad_flag, append_predefined_out=False
)
outs = []
for i, name in enumerate(self.outputs['names']):
outs.append(
out_type_map[self.outputs['types'][i]]
+ ' '
+ name.split('@')[0]
)
result = inputs_and_attrs + ', ' + ", ".join(outs)
return result
def gene_return_code(self):
return ""
def gene_api_declaration(
self, grad_flag=False, append_predefined_out=False
):
if not self.is_base_api and not self.is_only_composite_api:
invoke_func_name = self.invoke.split('(')[0]
if (not invoke_func_name.endswith("_grad")) and (
not invoke_func_name.endswith('_impl')
):
return ""
if self.is_only_composite_api:
return ""
api_func_name = self.get_api_func_name()
api_declaration = f"""
PADDLE_API void {api_func_name}({self.get_declare_args()});
"""
return api_declaration
def gene_kernel_backend_select(self):
all_no_need_buffer = True
for in_name in self.inputs['names']:
if in_name not in self.no_need_buffer:
all_no_need_buffer = False
if all_no_need_buffer:
return """
kernel_backend = ParseBackend(egr::Controller::Instance().GetExpectedPlace());
"""
else:
return super().gene_kernel_backend_select()
def get_return_type(self, inplace_flag=False):
return 'void'
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
kernel_output = []
output_names = []
output_create = ""
if len(out_dtype_list) == 1:
kernel_output.append('kernel_out')
output_names.append('kernel_out')
inplace_assign = (
" = " + self.inplace_map[self.outputs['names'][0]]
if inplace_flag
and self.inplace_map is not None
and self.outputs['names'][0] in self.inplace_map
else ""
)
output_create = ""
set_out_func = (
'SetKernelOutput'
if out_tensor_type_list is None
or out_tensor_type_list[0] == 'dense'
else 'SetSelectedRowsKernelOutput'
)
if out_dtype_list[0] == 'std::vector<Tensor>':
assert self.outputs['out_size_expr'] is not None, (
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
)
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}(&{self.outputs['names'][0]});"""
)
else:
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out = {set_out_func}({self.outputs['names'][0]});"""
)
elif len(out_dtype_list) > 1:
output_create = ""
for i, out_type_item in enumerate(out_dtype_list):
kernel_output.append(f'kernel_out_{i}')
output_names.append(f'kernel_out_{i}')
set_out_func = (
'SetKernelOutput'
if out_tensor_type_list is None
or out_tensor_type_list[i] == 'dense'
else 'SetSelectedRowsKernelOutput'
)
if out_type_item == 'Tensor':
if (
inplace_flag
and self.inplace_map is not None
and self.outputs['names'][i] in self.inplace_map
):
output_create = (
output_create
+ f"""
{code_indent} *{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
)
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}({self.outputs['names'][i]});"""
)
else:
if (
inplace_flag
and self.inplace_map is not None
and self.outputs['names'][i] in self.inplace_map
):
output_create = (
output_create
+ f"""
{code_indent} *{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
)
assert self.outputs['out_size_expr'][i] is not None, (
f"{self.api}: The out size expr : '{{expr}}' should be set when output has Tensor[]. You can refer 'split' api."
)
output_create = (
output_create
+ f"""
{code_indent} auto kernel_out_{i} = {set_out_func}(&{self.outputs['names'][i]});"""
)
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return kernel_output, output_names, output_create
def gene_invoke_code(self, invoke_code, params_code):
invoke_func_name = invoke_code.split('(')[0].strip()
if invoke_func_name.endswith('_grad') or invoke_func_name.endswith(
'_impl'
):
return f"""
PADDLE_API {self.get_return_type()} {self.api}({params_code}) {{
{invoke_code};
}}"""
else:
return ""
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path, fw_header_file_path):
return f"""
#include "{header_file_path}"
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_custom_impl.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/common/type_traits.h"
#include "paddle/phi/core/kernel_registry.h"
#include "{fw_header_file_path}"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/fusion.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
PD_DECLARE_bool(conv2d_disable_cudnn);
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def backward_api_namespace():
return (
"""
namespace paddle {
namespace experimental {
""",
"""
} // namespace experimental
} // namespace paddle
""",
)
def generate_backward_api(
backward_yaml_path,
is_fused_backward_yaml,
header_file_path,
source_file_path,
):
bw_apis = []
for each_api_yaml in backward_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
bw_apis.extend(api_list)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = backward_api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = (
"paddle/phi/api/backward/fused_backward_api_base.h"
if is_fused_backward_yaml
else "paddle/phi/api/backward/backward_api_base.h"
)
include_fw_header_file = (
"paddle/phi/api/include/fused_api.h"
if is_fused_backward_yaml
else "paddle/phi/api/include/api.h"
)
source_file.write(
source_include(include_header_file, include_fw_header_file)
)
source_file.write(namespace[0])
# not all fused ops support dygraph
if is_fused_backward_yaml is True:
new_bw_apis = [
bw_api
for bw_api in bw_apis
if "support_dygraph_mode" in bw_api
and bw_api["support_dygraph_mode"] is True
]
bw_apis = new_bw_apis
for bw_api in bw_apis:
bw_api = BackwardAPI(bw_api)
header_file.write(bw_api.gene_api_declaration())
source_file.write(bw_api.gene_api_code())
header_file.write(namespace[1])
source_file.write(namespace[1])
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ backward API files'
)
parser.add_argument(
'--backward_yaml_path',
help='path to backward yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/backward.yaml'],
)
parser.add_argument(
'--is_fused_backward_yaml',
help='flag of fused backward yaml',
action='store_true',
)
parser.add_argument(
'--backward_header_path',
help='output of generated backward header code file',
default='paddle/phi/api/backward/backward_api_base.h',
)
parser.add_argument(
'--backward_source_path',
help='output of generated backward source code file',
default='paddle/phi/api/lib/backward_api_base.cc',
)
options = parser.parse_args()
backward_yaml_path = options.backward_yaml_path
is_fused_backward_yaml = options.is_fused_backward_yaml
header_file_path = options.backward_header_path
source_file_path = options.backward_source_path
generate_backward_api(
backward_yaml_path,
is_fused_backward_yaml,
header_file_path,
source_file_path,
)
if __name__ == '__main__':
main()
File diff suppressed because it is too large Load Diff
+690
View File
@@ -0,0 +1,690 @@
# Copyright (c) 2023 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.
import argparse
import dist_api_gen
import yaml
from backward_api_gen import BackwardAPI
from dist_api_gen import DistForwardAPI
######################
# Code Gen Templates #
######################
MAIN_DIST_BRANCH_TEMPLATE = """
// Auto Parallel condition
if (run_auto_parallel) {{
// 1. InferSpmd (Infer DistAttr of Inputs&Outputs){}
// 2. Create Temporary Output & Prepare Dist and Dense Output{}
// 3. Infer DistTensor's Global Shape{}\n
// 4. Set Output Dist Attr For Default Impl{}\n
if (rank_is_in_current_mesh) {{
// 5. Select Kernel{}
// 6. Reshard Input{}\n
// 7. PrepareData (DataTransform & Prepare Dense Input){}
// 8. RecordOpInfoSupplement{}
// 9. Infer Local DenseTensor Meta{}
// 10. DenseTensor Kernel Call{}
// 11. Fallback{}
}}
// 12. Reshard Kernel Output to API output{}\n
// 13. Return
{}
}}
"""
# 1. Create API Outputs
SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD = """
auto dist_out = SetKernelDistOutput({});
auto dense_out = dist_out->unsafe_mutable_value();
"""
SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD = """
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out =
CreateKernelDistOutput({}, !rank_is_in_current_mesh, spmd_info.second[0]);
phi::distributed::DistTensor* dist_out = shared_dist_out.get();
phi::DenseTensor* dense_out = nullptr;
if (dist_out) {{
dense_out = dist_out->unsafe_mutable_value();
if (dense_out && !rank_is_in_current_mesh && !dist_out->defined()) {{
*dense_out = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
"""
SINGLE_OUT_CREATION_TEMPLATE = """
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out =
CreateKernelDistOutput({}, !rank_is_in_current_mesh);
phi::distributed::DistTensor* dist_out = shared_dist_out.get();
phi::DenseTensor* dense_out = nullptr;
if (dist_out) {{
dense_out = dist_out->unsafe_mutable_value();
if (dense_out && !rank_is_in_current_mesh && !dist_out->defined()) {{
*dense_out = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
"""
VECTOR_OUT_CREATION_TEMPLATE_WITH_NO_SPMD = """
auto dist_out = SetKernelDistOutput({name});
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
for (size_t i=0; i<dist_out.size(); i++) {{
if (dist_out[i]) {{
dense_out[i] = dist_out[i]->unsafe_mutable_value();
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
*dense_out[i] = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
}}
"""
VECTOR_OUT_CREATION_TEMPLATE_WITH_SPMD = """
auto shared_dist_out = CreateKernelDistOutput({name}, !rank_is_in_current_mesh, spmd_info.second[0]);
std::vector<phi::distributed::DistTensor*> dist_out;
for(auto& e: shared_dist_out){{
dist_out.push_back(e.get());
}}
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
for (size_t i=0; i<dist_out.size(); i++) {{
if (dist_out[i]) {{
dense_out[i] = dist_out[i]->unsafe_mutable_value();
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
*dense_out[i] = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
}}
"""
VECTOR_OUT_CREATION_TEMPLATE = """
auto shared_dist_out = CreateKernelDistOutput({name}, !rank_is_in_current_mesh);
std::vector<phi::distributed::DistTensor*> dist_out;
for(auto& e: shared_dist_out){{
dist_out.push_back(e.get());
}}
std::vector<phi::DenseTensor*> dense_out(dist_out.size(), nullptr);
for (size_t i=0; i<dist_out.size(); i++) {{
if (dist_out[i]) {{
dense_out[i] = dist_out[i]->unsafe_mutable_value();
if (dense_out[i] && !rank_is_in_current_mesh && !dist_out[i]->defined()) {{
*dense_out[i] = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
}}
"""
INPLACE_OUT_CREATION_TEMPLATE = """
*{} = {};
"""
MULTI_SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD = """
auto dist_out_{idx} = SetKernelDistOutput({name});
auto dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
*dense_out_{idx} = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
"""
MULTI_SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD = """
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out_{idx} =
CreateKernelDistOutput({name}, !rank_is_in_current_mesh, spmd_info.second[{idx}]);
phi::distributed::DistTensor* dist_out_{idx} = shared_dist_out_{idx}.get();
phi::DenseTensor* dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
*dense_out_{idx} = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
"""
MULTI_SINGLE_OUT_CREATION_TEMPLATE = """
std::shared_ptr<phi::distributed::DistTensor> shared_dist_out_{idx} =
CreateKernelDistOutput({name}, !rank_is_in_current_mesh);
phi::distributed::DistTensor* dist_out_{idx} = shared_dist_out_{idx}.get();
phi::DenseTensor* dense_out_{idx} = dist_out_{idx} ? dist_out_{idx}->unsafe_mutable_value() : nullptr;
if (dense_out_{idx} && !rank_is_in_current_mesh && !dist_out_{idx}->defined()) {{
*dense_out_{idx} = phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
"""
MULTI_VECTOR_OUT_CREATION_TEMPLATE = """
auto dist_out_{i} = SetKernelDistOutput({name});
std::vector<phi::DenseTensor*> dense_out_{i}(dist_out_{i}.size(), nullptr);
for (size_t i = 0; i < dist_out_{i}.size(); i++) {{
if (dist_out_{i}[i]) {{
dense_out_{i}[i] = const_cast<phi::DenseTensor*>(&dist_out_{i}[i]->value());
if (dense_out_{i}[i] && !rank_is_in_current_mesh && !dist_out_{i}[i]->defined()) {{
*dense_out_{i}[i]= phi::DenseTensor(
std::make_shared<phi::Allocation>(nullptr, 0, phi::distributed::GetDefaultPlace()),
phi::DenseTensorMeta());
}}
}}
}}
"""
# 9. Reshard Output
RESHARD_SINGLE_OUTPUT_TEMPLATE = """
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out, {}, "{}");"""
RESHARD_MULTI_SINGLE_OUTPUT_TEMPLATE = """
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out_{}, {}, "{}");"""
RESHARD_VECTOR_OUTPUT_TEMPLATE = """
ReshardKernelOutputToApiOutput(dev_ctx, shared_dist_out, {}, "{}");"""
NONEED_TO_RESHARD_OUTPUT_TEMPLATE = """
// API `{}` does not need to reshard output."""
SET_LOCAL_SHAPE_TEMPLATE = """
{meta_tensor}.set_dims(phi::make_ddim(local_shape));"""
class DistBackwardAPI(DistForwardAPI, BackwardAPI):
def __init__(self, backward_item_yaml):
BackwardAPI.__init__(self, backward_item_yaml)
self.forward_config = backward_item_yaml['forward']
self.init_dist_api_members()
# override DistForwardAPI's method
def generate_output_creation_code(self) -> str:
# backward api only need to generate kernel outputs
output_num = len(self.outputs['types'])
output_creation_code = ""
output_creation_code += "\n phi::DeviceContext* dev_ctx = nullptr;"
if output_num == 1:
self.dist_output_args.append('dist_out')
self.dense_output_args.append('dense_out')
if self.outputs['types'][0] == 'Tensor':
if self.infer_meta['spmd_rule'] is not None:
output_creation_code += (
SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
self.outputs['names'][0]
)
)
elif self.generate_general_infer_spmd is True:
output_creation_code += SINGLE_OUT_CREATION_TEMPLATE.format(
self.outputs['names'][0]
)
else:
output_creation_code += (
SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD.format(
self.outputs['names'][0]
)
)
elif self.outputs['types'][0] == 'std::vector<Tensor>':
if self.infer_meta['spmd_rule'] is not None:
output_creation_code += (
VECTOR_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
name=self.outputs['names'][0]
)
)
elif self.generate_general_infer_spmd is True:
output_creation_code += VECTOR_OUT_CREATION_TEMPLATE.format(
name=self.outputs['names'][0]
)
else:
output_creation_code += (
VECTOR_OUT_CREATION_TEMPLATE_WITH_NO_SPMD.format(
name=self.outputs['names'][0]
)
)
else:
self.vector_output_size_assertion_check()
elif output_num > 1:
for i, out_type in enumerate(self.outputs['types']):
self.dist_output_args.append(f'dist_out_{i}')
self.dense_output_args.append(f'dense_out_{i}')
if out_type == 'Tensor':
if self.infer_meta['spmd_rule'] is not None:
output_creation_code += (
MULTI_SINGLE_OUT_CREATION_TEMPLATE_WITH_SPMD.format(
name=self.outputs['names'][i], idx=i
)
)
elif self.generate_general_infer_spmd is True:
output_creation_code += (
MULTI_SINGLE_OUT_CREATION_TEMPLATE.format(
name=self.outputs['names'][i], idx=i
)
)
else:
output_creation_code += (
MULTI_SINGLE_OUT_CREATION_TEMPLATE_NO_SPMD.format(
name=self.outputs['names'][i], idx=i
)
)
elif out_type == 'std::vector<Tensor>':
output_creation_code += (
MULTI_VECTOR_OUT_CREATION_TEMPLATE.format(
i=i, name=self.outputs['names'][i]
)
)
else:
self.vector_output_size_assertion_check()
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return output_creation_code
def generate_bw_infer_local_shape_code(self, need_kernel=False):
arg_name = self.infer_meta['local_shape']
assert arg_name in self.outputs['names'], (
f"Auto Parallel will calculate local_shape for {arg_name} "
f"in {self.api}, but {arg_name} is not found in its outputs."
)
_, fw_inputs, fw_attrs, fw_outputs = self.parse_forward_config(
self.forward_config
)
# shape_type = self.attrs['attr_info'][shape_name][0]
# out_name = self.dist_output_args[0]
dist_out_name = self.dist_output_args[
self.outputs['names'].index(arg_name)
]
shape_type = self.get_shape_type(fw_attrs['attr_info'])
return_code = dist_api_gen.CALCULATE_LOCAL_SHAPE_TEMPLATE.format(
out_name=dist_out_name,
out_dist_attr=(
"PADDLE_GET_CONST(phi::distributed::TensorDistAttr, spmd_info.second[0]);"
if self.infer_meta['spmd_rule']
else f"phi::distributed::TensorDistAttr(common::vectorize({dist_out_name}->dims()))"
),
dtype=shape_type,
op_name=self.kernel['func'][0],
)
if need_kernel:
return (
dist_api_gen.CALCULATE_LOCAL_SHAPE_KERNEL_TEMPLATE.format(
out_grad_dist_attr=(
"PADDLE_GET_CONST(phi::distributed::TensorDistAttr, spmd_info.first[1]);"
if self.infer_meta['spmd_rule']
else "phi::distributed::TensorDistAttr(common::vectorize(out_grad.dims()))"
),
dtype=shape_type,
op_name=self.kernel['func'][0],
)
+ return_code
)
return return_code
def generate_infer_meta_code(self) -> str:
(
infer_meta_func_code,
input_args_code,
output_decl_code,
output_args_code,
) = self.generate_infer_meta_func_and_args_code()
infer_meta_code = ""
if self.infer_meta['global_shape'] is not None:
for i, out_name in enumerate(self.outputs['names']):
if out_name == self.infer_meta[
'global_shape'
] and self.need_to_generate_code_for_inplace_impl(i):
infer_meta_code += dist_api_gen.SET_DIMS_TEMPLATE.format(
dst=self.dist_output_args[i],
src=(
self.dist_output_args[i] + '_tmp'
if i > 0
else self.dist_output_args[i]
),
)
infer_meta_code = (
infer_meta_code
+ dist_api_gen.INFER_META_TEMPLATE.format(
infer_meta_func_code, input_args_code, output_args_code
)
)
# TODO(GhostScreaming): kernel like reshape need calculate local_shape
if self.infer_meta['local_shape'] is not None:
if (
self.kernel['param'] is not None
and self.infer_meta['local_shape'] not in self.kernel['param']
):
infer_meta_code += self.generate_bw_infer_local_shape_code()
else:
infer_meta_code += self.generate_bw_infer_local_shape_code(
need_kernel=True
)
infer_meta_code += SET_LOCAL_SHAPE_TEMPLATE.format(
meta_tensor="meta_" + self.dense_output_args[0]
)
return output_decl_code + infer_meta_code
# override DistForwardAPI's method
def generate_return_code(self) -> str:
return "return;"
# override BaseAPI's method
def get_api_func_name(self):
return self.api
# override BaseAPI's method
# The method lookup order are: (DistBackwardAPI.__mro__)
# <class '__main__.DistBackwardAPI'>,
# <class 'dist_api_gen.DistForwardAPI'>,
# <class 'api_gen.ForwardAPI'>,
# <class 'backward_api_gen.BackwardAPI'>,
# <class 'api_base.BaseAPI'>,
# <class 'object'>
# if don't override it, the ForwardAPI's gene_output will be called
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
return BackwardAPI.gene_output(
self,
out_dtype_list,
out_tensor_type_list,
code_indent,
inplace_flag,
)
# override BaseAPI's method
def get_return_type(self, inplace_flag=False):
return BackwardAPI.get_return_type(self)
# override BaseAPI's method
def gene_return_code(self):
return ""
# override BaseAPI's method
def gene_api_declaration(
self, grad_flag=False, append_predefined_out=False
) -> str:
return BackwardAPI.gene_api_declaration(
self, grad_flag=grad_flag, append_predefined_out=not grad_flag
)
def generate_reshard_output_code(self):
reshard_output_code = ""
if self.generate_infer_spmd is True:
output_num = len(self.outputs['types'])
if output_num == 1:
if self.outputs['types'][0] == 'Tensor':
reshard_output_code += (
RESHARD_SINGLE_OUTPUT_TEMPLATE.format(
self.outputs['names'][0], self.outputs['names'][0]
)
)
elif self.outputs['types'][0] == 'std::vector<Tensor>':
reshard_output_code += (
RESHARD_VECTOR_OUTPUT_TEMPLATE.format(
self.outputs['names'][0], self.outputs['names'][0]
)
)
else:
self.vector_output_size_assertion_check()
elif output_num > 1:
for i, out_type in enumerate(self.outputs['types']):
if out_type == 'Tensor':
reshard_output_code += (
RESHARD_MULTI_SINGLE_OUTPUT_TEMPLATE.format(
i,
self.outputs['names'][i],
self.outputs['names'][i],
)
)
else:
self.vector_output_size_assertion_check()
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
else:
reshard_output_code += NONEED_TO_RESHARD_OUTPUT_TEMPLATE.format(
self.kernel['func'][0]
)
# do nothing
pass
return reshard_output_code
def generate_auto_parallel_branch(self) -> str:
# if no tensor input, do not generate auto parallel branch
if len(self.inputs['names']) == 0:
return ""
infer_spmd_code = self.generate_infer_spmd_code()
output_creation_code = self.generate_output_creation_code()
infer_global_shape_code = self.generate_infer_global_shape_code()
output_dist_attr_setting = self.generate_output_dist_attr_setting()
kernel_selection_code = self.generate_kernel_selection_code()
reshard_input_code = self.generate_reshard_input_code()
(
prepare_data_code,
input_name_tensor_map,
) = self.generate_prepare_data_code()
record_op_info_supplement_code = (
self.generate_record_op_info_supplement(
input_name_tensor_map, ' ', True
)
)
infer_meta_code = self.generate_infer_meta_code()
kernel_call_code = self.generate_kernel_call_code(is_forward=False)
fallback_code = self.generate_fallback_code()
reshard_output_code = self.generate_reshard_output_code()
return_code = self.generate_return_code()
return MAIN_DIST_BRANCH_TEMPLATE.format(
infer_spmd_code,
output_creation_code,
infer_global_shape_code,
output_dist_attr_setting,
kernel_selection_code,
reshard_input_code,
prepare_data_code,
record_op_info_supplement_code,
infer_meta_code,
kernel_call_code,
fallback_code,
reshard_output_code,
return_code,
)
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path, fw_header_file_path):
return f"""
#include "{header_file_path}"
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_custom_impl.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/common/type_traits.h"
#include "paddle/phi/core/kernel_registry.h"
#include "{fw_header_file_path}"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/fusion.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/nccl_comm_context.h"
#elif defined(PADDLE_WITH_XPU_BKCL)
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/xccl_comm_context.h"
#endif
#ifdef PADDLE_WITH_DISTRIBUTE
#include "paddle/phi/core/distributed/store/store_utils.h"
#include "paddle/phi/infermeta/spmd_rules/rules.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#endif
PD_DECLARE_bool(conv2d_disable_cudnn);
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def backward_api_namespace():
return (
"""
namespace paddle {
namespace experimental {
""",
"""
} // namespace experimental
} // namespace paddle
""",
)
def generate_backward_api(
backward_yaml_path,
is_fused_backward_yaml,
header_file_path,
source_file_path,
):
bw_apis = []
for each_api_yaml in backward_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
bw_apis.extend(api_list)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = backward_api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = (
"paddle/phi/api/backward/fused_backward_api_base.h"
if is_fused_backward_yaml
else "paddle/phi/api/backward/backward_api_base.h"
)
include_fw_header_file = (
"paddle/phi/api/include/fused_api.h"
if is_fused_backward_yaml
else "paddle/phi/api/include/api.h"
)
source_file.write(
source_include(include_header_file, include_fw_header_file)
)
source_file.write(namespace[0])
# not all fused ops support dygraph
if is_fused_backward_yaml is True:
new_bw_apis = [
bw_api
for bw_api in bw_apis
if "support_dygraph_mode" in bw_api
and bw_api["support_dygraph_mode"] is True
]
bw_apis = new_bw_apis
for bw_api in bw_apis:
dist_bw_api = DistBackwardAPI(bw_api)
header_file.write(dist_bw_api.gene_api_declaration())
if is_fused_backward_yaml is True:
source_file.write(dist_bw_api.gene_api_code())
else:
source_file.write(dist_bw_api.gene_api_code())
header_file.write(namespace[1])
source_file.write(namespace[1])
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ backward API files'
)
parser.add_argument(
'--backward_yaml_path',
help='path to backward yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/backward.yaml'],
)
parser.add_argument(
'--is_fused_backward_yaml',
help='flag of fused backward yaml',
action='store_true',
)
parser.add_argument(
'--backward_header_path',
help='output of generated backward header code file',
default='paddle/phi/api/backward/backward_api_base.h',
)
parser.add_argument(
'--backward_source_path',
help='output of generated backward source code file',
default='paddle/phi/api/lib/backward_api_base.cc',
)
options = parser.parse_args()
backward_yaml_path = options.backward_yaml_path
is_fused_backward_yaml = options.is_fused_backward_yaml
header_file_path = options.backward_header_path
source_file_path = options.backward_source_path
generate_backward_api(
backward_yaml_path,
is_fused_backward_yaml,
header_file_path,
source_file_path,
)
if __name__ == '__main__':
main()
@@ -0,0 +1,214 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import yaml
from api_gen import ForwardAPI, backward_api_black_list
from dist_api_gen import DistForwardAPI
from sparse_api_gen import SparseAPI
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path):
return f"""#include "{header_file_path}"
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_custom_impl.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/multiary.h"
#include "paddle/phi/infermeta/nullary.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/ternary.h"
#include "paddle/phi/infermeta/sparse/unary.h"
#include "paddle/phi/infermeta/sparse/binary.h"
#include "paddle/phi/infermeta/sparse/multiary.h"
#include "paddle/phi/api/profiler/event_tracing.h"
#include "paddle/phi/api/profiler/supplement_tracing.h"
#ifdef PADDLE_WITH_DISTRIBUTE
#include "paddle/phi/infermeta/spmd_rules/rules.h"
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
#endif
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def api_namespace():
return (
"""
namespace paddle {
namespace experimental {
""",
"""
} // namespace experimental
} // namespace paddle
""",
)
def sparse_namespace():
return (
"""
namespace sparse {
""",
"""
} // namespace sparse
""",
)
def generate_intermediate_api(
api_yaml_path,
sparse_api_yaml_path,
dygraph_header_file_path,
dygraph_source_file_path,
gen_dist_branch,
):
dygraph_header_file = open(dygraph_header_file_path, 'w')
dygraph_source_file = open(dygraph_source_file_path, 'w')
namespace = api_namespace()
sparse_namespace_pair = sparse_namespace()
dygraph_header_file.write("#pragma once\n")
dygraph_header_file.write(header_include())
dygraph_header_file.write(namespace[0])
dygraph_include_header_file = "paddle/phi/api/lib/dygraph_api.h"
dygraph_source_file.write(source_include(dygraph_include_header_file))
dygraph_source_file.write(namespace[0])
apis = []
for each_api_yaml in api_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
apis.extend(api_list)
for api in apis:
forward_api = (
DistForwardAPI(api) if gen_dist_branch else ForwardAPI(api)
)
if forward_api.is_dygraph_api:
dygraph_header_file.write(forward_api.gene_api_declaration())
dygraph_source_file.write(forward_api.gene_api_code())
dygraph_header_file.write(sparse_namespace_pair[0])
dygraph_source_file.write(sparse_namespace_pair[0])
sparse_apis = []
for each_sparse_api_yaml in sparse_api_yaml_path:
with open(each_sparse_api_yaml, 'r') as f:
sparse_api_list = yaml.load(f, Loader=yaml.FullLoader)
if sparse_api_list:
sparse_apis.extend(sparse_api_list)
for api in sparse_apis:
sparse_api = SparseAPI(api)
if sparse_api.api in backward_api_black_list:
continue
if sparse_api.is_dygraph_api:
dygraph_header_file.write(sparse_api.gene_api_declaration())
dygraph_source_file.write(sparse_api.gene_api_code())
dygraph_header_file.write(sparse_namespace_pair[1])
dygraph_header_file.write(namespace[1])
dygraph_source_file.write(sparse_namespace_pair[1])
dygraph_source_file.write(namespace[1])
dygraph_header_file.close()
dygraph_source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ Sparse API files'
)
parser.add_argument(
'--api_yaml_path',
nargs='+',
help='path to api yaml file',
default=['paddle/phi/ops/yaml/ops.yaml'],
)
parser.add_argument(
'--sparse_api_yaml_path',
nargs='+',
help='path to sparse api yaml file',
default='paddle/phi/ops/yaml/sparse_ops.yaml',
)
parser.add_argument(
'--dygraph_api_header_path',
help='output of generated dygraph api header code file',
default='paddle/phi/api/lib/dygraph_api.h',
)
parser.add_argument(
'--dygraph_api_source_path',
help='output of generated dygraph api source code file',
default='paddle/phi/api/lib/dygraph_api.cc',
)
parser.add_argument(
'--gen_dist_branch',
help='whether generate distributed branch code',
dest='gen_dist_branch',
action='store_true',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
sparse_api_yaml_path = options.sparse_api_yaml_path
dygraph_header_file_path = options.dygraph_api_header_path
dygraph_source_file_path = options.dygraph_api_source_path
gen_dist_branch = options.gen_dist_branch
generate_intermediate_api(
api_yaml_path,
sparse_api_yaml_path,
dygraph_header_file_path,
dygraph_source_file_path,
gen_dist_branch,
)
if __name__ == '__main__':
main()
+584
View File
@@ -0,0 +1,584 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import yaml
from api_base import PREFIX_TENSOR_NAME
from api_gen import ForwardAPI, backward_api_black_list
class SparseAPI(ForwardAPI):
def __init__(self, api_item_yaml):
super().__init__(api_item_yaml)
def gene_api_declaration(
self, grad_flag=False, append_predefined_out=False
):
return f"""
// {", ".join(self.outputs['names'])}
{super().gene_api_declaration(append_predefined_out=False)}
"""
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
kernel_output = []
output_names = []
output_create = ""
return_type = self.get_return_type_with_intermediate(inplace_flag)
output_type_map = {
'dense': 'TensorType::DENSE_TENSOR',
'sparse_coo': 'TensorType::SPARSE_COO',
'sparse_csr': 'TensorType::SPARSE_CSR',
}
if len(out_dtype_list) == 1:
kernel_output.append('kernel_out')
output_names.append('kernel_out')
inplace_assign = (
" = " + self.inplace_map[self.outputs['names'][0]]
if inplace_flag
and self.inplace_map is not None
and self.outputs['names'][0] in self.inplace_map
else ""
)
output_create = f"""
{return_type} api_output{inplace_assign};
auto* kernel_out = SetSparseKernelOutput(&api_output, {output_type_map[out_dtype_list[0]]});"""
elif len(out_dtype_list) > 1:
output_create = f"""
{return_type} api_output;"""
if inplace_flag:
output_create = f"""
{return_type} api_output{{"""
for out_name in self.outputs['names']:
if out_name in self.inplace_map:
output_create = (
output_create + self.inplace_map[out_name] + ', '
)
else:
output_create += 'Tensor(), '
output_create = output_create[:-2] + '};'
for i in range(len(out_dtype_list)):
kernel_output.append(f'kernel_out_{i}')
output_names.append(f'kernel_out_{i}')
output_create = (
output_create
+ f"""
auto* kernel_out_{i} = SetSparseKernelOutput(&std::get<{i}>(api_output), {output_type_map[out_dtype_list[i]]});"""
)
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return kernel_output, output_names, output_create
def gen_sparse_kernel_context(self, kernel_output_names):
input_trans_map = {
'const Tensor&': 'const phi::TenseBase&',
'const std::vector<Tensor>&': 'const std::vector<phi::TenseBase>&',
'const paddle::optional<Tensor>&': 'paddle::optional<const phi::TenseBase&>',
}
out_trans_map = {
'Tensor': 'phi::TenseBase*',
'std::vector<Tensor>': 'std::vector<phi::TenseBase*>',
}
input_names = self.inputs['names']
input_infos = self.inputs['input_info']
input_types = self.inputs['tensor_type']
tensor_type_map = {
'dense': 'phi::DenseTensor',
'sparse_coo': 'phi::SparseCooTensor',
'sparse_csr': 'phi::SparseCsrTensor',
}
inputsname2tensortype = {}
for i in range(len(input_names)):
inputsname2tensortype[input_names[i]] = input_types[i]
attr_names = self.attrs['names']
kernel_param = self.kernel['param']
if kernel_param is None:
kernel_param = input_names + attr_names
infer_meta = self.infer_meta
infer_meta_params = (
infer_meta['param']
if infer_meta['param'] is not None
else input_names + attr_names
)
kernel_context_code = ""
for param in kernel_param:
if param in input_names and param not in infer_meta_params:
var_name = " auto " + PREFIX_TENSOR_NAME + param + " = "
if self.inputs['input_info'][param] == "const Tensor&":
if inputsname2tensortype[param] == "sparse_coo":
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForSparseCooTensor("
+ param
+ ");\n"
)
elif inputsname2tensortype[param] == "sparse_csr":
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForSparseCsrTensor("
+ param
+ ");\n"
)
else:
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForDenseTensorInSparse("
+ param
+ ");\n"
)
elif param in self.optional_vars:
tensor_type = 'phi::DenseTensor'
for name, input_type in zip(input_names, input_types):
if param == name:
tensor_type = tensor_type_map[input_type]
break
optional_var = "paddle::optional<" + tensor_type + ">("
if inputsname2tensortype[param] == "sparse_coo":
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForSparseCooTensor("
+ param
+ ");\n"
)
elif inputsname2tensortype[param] == "sparse_csr":
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForSparseCsrTensor("
+ param
+ ");\n"
)
else:
kernel_context_code = (
kernel_context_code
+ var_name
+ "PrepareDataForDenseTensorInSparse("
+ param
+ ");\n"
)
for param in kernel_param:
if param in input_names:
if param in self.optional_vars:
kernel_context_code = (
kernel_context_code
+ f"""
kernel_context.EmplaceBackInput({param} ? &(*{PREFIX_TENSOR_NAME}{param}) : nullptr);"""
)
else:
kernel_context_code = (
kernel_context_code
+ f"""
kernel_context.EmplaceBackInput({PREFIX_TENSOR_NAME}{param}.get());"""
)
continue
if param in attr_names:
# set attr for kernel_context
if 'IntArray' in self.attrs['attr_info'][param][0]:
param = 'phi::IntArray(' + param + ')'
elif 'Scalar' in self.attrs['attr_info'][param][0]:
param = 'phi::Scalar(' + param + ')'
elif isinstance(param, bool):
param = str(param).lower()
else:
param + str(param) + ", "
kernel_context_code = (
kernel_context_code
+ f"""
kernel_context.EmplaceBackAttr({param});"""
)
for out_name in kernel_output_names:
kernel_context_code = (
kernel_context_code
+ f"""
kernel_context.EmplaceBackOutput({out_name});"""
)
return kernel_context_code
def prepare_input(self):
input_names = self.inputs['names']
input_types = self.inputs['tensor_type']
attr_names = self.attrs['names']
infer_meta = self.infer_meta
infer_meta_params = (
infer_meta['param']
if infer_meta['param'] is not None
else input_names + attr_names
)
inputsname2tensortype = {}
for i in range(len(input_names)):
inputsname2tensortype[input_names[i]] = input_types[i]
create_input_var_code = ""
tensor_type_map = {
'dense': 'phi::DenseTensor',
'sparse_coo': 'phi::SparseCooTensor',
'sparse_csr': 'phi::SparseCsrTensor',
}
for param in infer_meta_params:
if param in input_names:
var_name = " auto " + PREFIX_TENSOR_NAME + param + " = "
if self.inputs['input_info'][param] == "const Tensor&":
if inputsname2tensortype[param] == "sparse_coo":
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForSparseCooTensor("
+ param
+ ");\n"
)
elif inputsname2tensortype[param] == "sparse_csr":
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForSparseCsrTensor("
+ param
+ ");\n"
)
else:
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForDenseTensorInSparse("
+ param
+ ");\n"
)
elif param in self.optional_vars:
tensor_type = 'phi::DenseTensor'
for name, input_type in zip(input_names, input_types):
if param == name:
tensor_type = tensor_type_map[input_type]
break
optional_var = "paddle::optional<" + tensor_type + ">("
if inputsname2tensortype[param] == "sparse_coo":
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForSparseCooTensor("
+ param
+ ");\n"
)
elif inputsname2tensortype[param] == "sparse_csr":
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForSparseCsrTensor("
+ param
+ ");\n"
)
else:
create_input_var_code = (
create_input_var_code
+ var_name
+ "PrepareDataForDenseTensorInSparse("
+ param
+ ");\n"
)
return f"""{create_input_var_code}"""
def gen_sparse_kernel_code(self, kernel_name, inplace_flag=False):
_, kernel_output_names, output_create = self.gene_output(
self.kernel['dispatch'][kernel_name][1], None, '', inplace_flag
)
kernel_context_code = self.gen_sparse_kernel_context(
kernel_output_names
)
return_code = (
""
if len(self.gene_return_code()) == 0
else " " + self.gene_return_code()
)
return f"""
VLOG(6) << "{self.api} api sparse kernel key: [" << kernel_backend << ", " << kernel_layout << ", "<< kernel_data_type << "]";
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
"{kernel_name}", {{kernel_backend, kernel_layout, kernel_data_type}});
const auto& phi_kernel = kernel_result.kernel;
if (FLAGS_low_precision_op_list) {{
phi::KernelFactory::Instance().AddToLowPrecisionKernelList("{self.api}", kernel_data_type);
}}
VLOG(6) << "{self.api} api sparse kernel: " << phi_kernel;
auto* dev_ctx = GetDeviceContextByBackend(kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
auto kernel_context = phi::KernelContext(dev_ctx);
{output_create}
{self.prepare_input()}
{self.gene_infer_meta(kernel_output_names, '')}
{kernel_context_code}
phi_kernel(&kernel_context);
if (FLAGS_benchmark) {{
dev_ctx->Wait();
std::cout << \"{self.api} kernel run finish.\" << std::endl;
}}
{return_code}"""
def get_condition_code(self, kernel_name):
assert self.kernel['dispatch'][kernel_name], (
f"{self.api} api: the tensor type of inputs and outputs for kernel isn't set, see also 'kernel:func' of 'conv3d' in sparse_ops.yaml."
)
input_types = self.kernel['dispatch'][kernel_name][0]
sparse_type_map = {
'sparse_coo': 'DataLayout::SPARSE_COO',
'sparse_csr': 'DataLayout::SPARSE_CSR',
}
condition_list = []
tensor_type_list = []
for i, in_type in enumerate(input_types):
if in_type == "dense":
if self.inputs['names'][i] in self.optional_vars:
condition_list.append(
f"(!{self.inputs['names'][i]} || phi::DenseTensor::classof({self.inputs['names'][i]}->impl().get()))"
)
else:
condition_list.append(
f"phi::DenseTensor::classof({self.inputs['names'][i]}.impl().get())"
)
else:
if in_type == 'sparse_coo':
condition_list.append(
f"{self.inputs['names'][i]}.is_sparse_coo_tensor()"
)
else:
condition_list.append(
f"{self.inputs['names'][i]}.is_sparse_csr_tensor()"
)
tensor_type_list.append(in_type)
self.inputs['tensor_type'] = tensor_type_list
return " && ".join(condition_list)
def gene_dispatch_code(self, kernel_name, inplace_flag=False):
return f"""
if ({self.get_condition_code(kernel_name)}) {{
{self.gen_sparse_kernel_code(kernel_name, inplace_flag)}
}}
"""
def gene_base_api_code(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
api_func_name = self.get_api_func_name()
if inplace_flag and api_func_name[-1] != '_':
api_func_name += '_'
kernel_dispatch_code = f"{self.gene_kernel_select()}\n"
for kernel_name in self.kernel['func']:
kernel_dispatch_code += self.gene_dispatch_code(
kernel_name, inplace_flag
)
return f"""
PADDLE_API {self.get_return_type(inplace_flag)} {api_func_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=False)}) {{
{kernel_dispatch_code}
PADDLE_THROW(common::errors::Unimplemented(
"The kernel of ({self.api}) for input tensors is unimplemented, please check the type of input tensors."));
}}
"""
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path):
return f"""
#include "{header_file_path}"
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/ternary.h"
#include "paddle/phi/infermeta/multiary.h"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/utils/none.h"
#include "paddle/phi/infermeta/sparse/unary.h"
#include "paddle/phi/infermeta/sparse/binary.h"
#include "paddle/phi/infermeta/sparse/multiary.h"
#include "paddle/phi/infermeta/sparse/backward.h"
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def api_namespace():
return (
"""
namespace paddle {
namespace experimental {
namespace sparse {
""",
"""
} // namespace sparse
} // namespace experimental
} // namespace paddle
""",
)
def generate_api(
api_yaml_path, header_file_path, source_file_path, grad_flag=False
):
apis = []
for each_api_yaml in api_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
apis.extend(api_list)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = "paddle/phi/api/include/sparse_api.h"
source_file.write(source_include(include_header_file))
source_file.write(namespace[0])
for api in apis:
sparse_api = SparseAPI(api)
if sparse_api.api in backward_api_black_list:
continue
if sparse_api.is_dygraph_api:
sparse_api.is_dygraph_api = False
header_file.write(
sparse_api.gene_api_declaration(
grad_flag=grad_flag, append_predefined_out=False
)
)
source_file.write(
sparse_api.gene_api_code(
grad_flag=grad_flag, append_predefined_out=False
)
)
header_file.write(namespace[1])
source_file.write(namespace[1])
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ Sparse API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to sparse api yaml file',
nargs='+',
default='paddle/phi/ops/yaml/sparse_ops.yaml',
)
parser.add_argument(
'--api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/include/sparse_api.h',
)
parser.add_argument(
'--api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/sparse_api.cc',
)
parser.add_argument(
'--backward_api_yaml_path',
help='path to sparse api yaml file',
nargs='+',
default='paddle/phi/ops/yaml/sparse_backward_ops.yaml',
)
parser.add_argument(
'--backward_api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/backward/sparse_backward_api.h',
)
parser.add_argument(
'--backward_api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/sparse_backward_api.cc',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
header_file_path = options.api_header_path
source_file_path = options.api_source_path
backward_api_yaml_path = options.backward_api_yaml_path
backward_header_file_path = options.backward_api_header_path
backward_source_file_path = options.backward_api_source_path
generate_api(
api_yaml_path, header_file_path, source_file_path, grad_flag=False
)
generate_api(
backward_api_yaml_path,
backward_header_file_path,
backward_source_file_path,
grad_flag=True,
)
if __name__ == '__main__':
main()
@@ -0,0 +1,244 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import yaml
from backward_api_gen import BackwardAPI
from sparse_api_gen import SparseAPI
class SparseBackwardAPI(SparseAPI, BackwardAPI):
def __init__(self, bw_api_item_yaml):
BackwardAPI.__init__(self, bw_api_item_yaml)
def get_api_func_name(self):
return self.api
def gene_kernel_backend_select(self):
return BackwardAPI.gene_kernel_backend_select(self)
def get_return_type(self, inplace_flag=False):
return BackwardAPI.get_return_type(self)
def gene_return_code(self):
return "return;"
def gene_api_declaration(
self, grad_flag=False, append_predefined_out=False
):
return SparseAPI.gene_api_declaration(
self, grad_flag=grad_flag, append_predefined_out=False
)
def get_declare_args(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
return BackwardAPI.get_declare_args(
self, grad_flag=grad_flag, append_predefined_out=False
)
def get_define_args(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
return BackwardAPI.get_define_args(
self, grad_flag=grad_flag, append_predefined_out=False
)
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
kernel_output = []
output_names = []
output_create = ""
output_type_map = {
'dense': 'TensorType::DENSE_TENSOR',
'sparse_coo': 'TensorType::SPARSE_COO',
'sparse_csr': 'TensorType::SPARSE_CSR',
}
if len(out_dtype_list) == 1:
kernel_output.append('kernel_out')
output_names.append('kernel_out')
inplace_assign = (
" = " + self.inplace_map[self.outputs['names'][0]]
if inplace_flag
and self.inplace_map is not None
and self.outputs['names'][0] in self.inplace_map
else ""
)
output_create = f"""
auto kernel_out = SetSparseKernelOutput({self.outputs['names'][0]}, {output_type_map[out_dtype_list[0]]});"""
elif len(out_dtype_list) > 1:
output_create = ""
for i, out_type_item in enumerate(out_dtype_list):
kernel_output.append(f'kernel_out_{i}')
output_names.append(f'kernel_out_{i}')
if (
inplace_flag
and self.inplace_map is not None
and self.outputs['names'][i] in self.inplace_map
):
output_create = (
output_create
+ f"""
*{self.outputs['names'][i]} = {self.inplace_map[self.outputs['names'][i]]};"""
)
output_create = (
output_create
+ f"""
auto kernel_out_{i} = SetSparseKernelOutput({self.outputs['names'][i]}, {output_type_map[out_dtype_list[i]]});"""
)
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return kernel_output, output_names, output_create
def header_include():
return """
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path):
return f"""
#include "{header_file_path}"
#include <memory>
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/include/sparse_api.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/api/lib/data_transform.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/infermeta/sparse/unary.h"
#include "paddle/phi/infermeta/sparse/binary.h"
#include "paddle/phi/infermeta/sparse/backward.h"
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def api_namespace():
return (
"""
namespace paddle {
namespace experimental {
namespace sparse {
""",
"""
} // namespace sparse
} // namespace experimental
} // namespace paddle
""",
)
def generate_api(
api_yaml_path, header_file_path, source_file_path, grad_flag=False
):
with open(api_yaml_path, 'r') as f:
apis = yaml.load(f, Loader=yaml.FullLoader)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = "paddle/phi/api/backward/sparse_backward_api_base.h"
source_file.write(source_include(include_header_file))
source_file.write(namespace[0])
for api in apis:
sparse_bw_api = SparseBackwardAPI(api)
header_file.write(
sparse_bw_api.gene_api_declaration(
grad_flag=grad_flag, append_predefined_out=False
)
)
source_file.write(
sparse_bw_api.gene_api_code(
grad_flag=grad_flag, append_predefined_out=False
)
)
header_file.write(namespace[1])
source_file.write(namespace[1])
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ Sparse API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to sparse api yaml file',
default='paddle/phi/ops/yaml/sparse_backward.yaml',
)
parser.add_argument(
'--api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/backward/sparse_backward_api_base.h',
)
parser.add_argument(
'--api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/sparse_backward_api_base.cc',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
header_file_path = options.api_header_path
source_file_path = options.api_source_path
generate_api(
api_yaml_path, header_file_path, source_file_path, grad_flag=True
)
if __name__ == '__main__':
main()
+431
View File
@@ -0,0 +1,431 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import yaml
from api_gen import ForwardAPI
PREFIX_TENSOR_NAME = 'input_'
PREFIX_META_TENSOR_NAME = 'meta_'
class StringsAPI(ForwardAPI):
def __init__(self, api_item_yaml):
super().__init__(api_item_yaml)
def get_api_func_name(self):
return self.api
def gene_api_declaration(self):
return f"""
// {", ".join(self.outputs['names'])}
{super().gene_api_declaration(append_predefined_out=False)}
"""
def get_kernel_tensor_out_type(self, output_name):
strings_type = 'TensorType::DENSE_TENSOR'
if output_name.endswith('@StringTensor'):
strings_type = 'TensorType::STRING_TENSOR'
return strings_type
def get_tensor_type(self, kernel_tensor_out_type):
tensor_type_dict = {
"TensorType::DENSE_TENSOR": "phi::DenseTensor",
"TensorType::STRING_TENSOR": "phi::StringTensor",
}
return tensor_type_dict[kernel_tensor_out_type]
def gene_output(
self,
out_dtype_list,
out_tensor_type_list=None,
code_indent='',
inplace_flag=False,
):
kernel_output = []
output_names = []
output_create = ""
return_type = self.get_return_type(inplace_flag)
if len(out_dtype_list) == 1:
kernel_output.append('kernel_out')
output_names.append('kernel_out')
kernel_tensor_out_type = self.get_kernel_tensor_out_type(
self.outputs['names'][0]
)
tensor_type = self.get_tensor_type(kernel_tensor_out_type)
inplace_assign = (
" = " + self.inplace_map[self.outputs['names'][0]]
if inplace_flag
and self.inplace_map is not None
and self.outputs['names'][0] in self.inplace_map
else ""
)
output_create = f"""
{return_type} api_output{inplace_assign};
{tensor_type}* kernel_out = dynamic_cast<{tensor_type}*>(SetStringsKernelOutput(&api_output, {kernel_tensor_out_type}));"""
elif len(out_dtype_list) > 1:
output_create = f"""
{return_type} api_output;"""
for i in range(len(out_dtype_list)):
kernel_output.append(f'kernel_out_{i}')
output_names.append(f'kernel_out_{i}')
kernel_tensor_out_type = self.get_kernel_tensor_out_type(
self.outputs['names'][i]
)
tensor_type = self.get_tensor_type(kernel_tensor_out_type)
if (
inplace_flag
and self.inplace_map is not None
and self.outputs['names'][i] in self.inplace_map
):
output_create = (
output_create
+ f"""
std::get<{i}>(api_output) = {self.inplace_map[self.outputs['names'][i]]};"""
)
output_create = (
output_create
+ f"""
{tensor_type}* kernel_out_{i} = dynamic_cast<{tensor_type}*>(SetStringsKernelOutput(&std::get<{i}>(api_output), {kernel_tensor_out_type}));"""
)
else:
raise ValueError(
f"{self.api} : Output error: the output should not be empty."
)
return kernel_output, output_names, output_create
def get_kernel_args(self, code_indent):
input_trans_map = {
'const Tensor&': 'const phi::StringTensor&',
'const std::vector<Tensor>&': 'const std::vector<const phi::StringTensor*>&',
'const paddle::optional<Tensor>&': 'paddle::optional<const phi::StringTensor&>',
'const paddle::optional<std::vector<Tensor>>&': 'paddle::optional<const std::vector<phi::StringTensor>&>',
}
out_trans_map = {
'Tensor': 'phi::StringTensor*',
'std::vector<Tensor>': 'std::vector<phi::StringTensor*>&',
}
input_names = self.inputs['names']
input_infos = self.inputs['input_info']
kernel_args_type_list = ['const phi::DeviceContext&']
attr_names = self.attrs['names']
kernel_param = self.kernel['param']
if kernel_param is None:
kernel_param = input_names + attr_names
input_tensor_code = ""
# set input_tensor_code
for i, input_name in enumerate(input_names):
input_tensor_code = (
input_tensor_code
+ f"""
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = TensorToStringTensor({input_name});"""
)
# set kernel_args
kernel_args = "*dev_ctx, "
for param in kernel_param:
if param in input_names:
if param in self.optional_vars:
kernel_args = (
kernel_args + PREFIX_TENSOR_NAME + param + ", "
)
else:
if self.inputs['input_info'][param] == "const Tensor&":
kernel_args = (
kernel_args
+ "*"
+ PREFIX_TENSOR_NAME
+ param
+ ", "
)
elif (
self.inputs['input_info'][input_name]
== "const std::vector<Tensor>&"
):
kernel_args = (
kernel_args + PREFIX_TENSOR_NAME + param + ", "
)
else:
# do nothing
pass
kernel_in_type = input_trans_map[input_infos[param]]
kernel_args_type_list.append(kernel_in_type)
elif param in attr_names:
# set attr for kernel_context
if 'IntArray' in self.attrs['attr_info'][param][0]:
kernel_args_type_list.append('const phi::IntArray&')
param = 'phi::IntArray(' + param + ')'
elif 'Scalar' in self.attrs['attr_info'][param][0]:
kernel_args_type_list.append('const phi::Scalar&')
param = 'phi::Scalar(' + param + ')'
else:
kernel_args_type_list.append(
self.attrs['attr_info'][param][0]
)
kernel_args = kernel_args + param + ", "
elif isinstance(param, bool):
kernel_args = kernel_args + str(param).lower() + ", "
else:
kernel_args = kernel_args + str(param) + ", "
for out_type in self.outputs['types']:
kernel_args_type_list.append(out_trans_map[out_type])
# set kernel_signature
kernel_signature = "void(*)(" + ", ".join(kernel_args_type_list) + ")"
return input_tensor_code, kernel_args[:-2], kernel_signature
def gen_string_tensor_kernel_code(self, inplace_flag=False, code_indent=""):
input_tensors, kernel_args, kernel_signature = self.get_kernel_args(
code_indent
)
outputs_args, kernel_output_names, output_create = self.gene_output(
self.outputs['types'], None, '', inplace_flag
)
return f"""
// 1. Get kernel signature and kernel
VLOG(6) << "{self.api} api strings kernel key: [" << kernel_backend << ", " << kernel_layout << ", "<< kernel_data_type << "]";
auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
"{self.kernel['func'][0]}", {{kernel_backend, kernel_layout, kernel_data_type}});
if (FLAGS_low_precision_op_list) {{
phi::KernelFactory::Instance().AddToLowPrecisionKernelList("{self.api}", kernel_data_type);
}}
const auto& kernel = kernel_result.kernel;
VLOG(6) << "{self.api} api strings kernel: " << kernel;
// 2. Get Device Context and input
auto* dev_ctx = GetDeviceContextByBackend(kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend);
{input_tensors}
// 3. Set output
{output_create}
{self.gene_infer_meta(kernel_output_names, code_indent)}
// 4. run kernel
{code_indent} using kernel_signature = {kernel_signature};
{code_indent} auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
{code_indent} (*kernel_fn)({kernel_args}, {", ".join(outputs_args)});
{code_indent} if (FLAGS_benchmark) {{
{code_indent} dev_ctx->Wait();
{code_indent} std::cout << \"{self.api} kernel run finish.\" << std::endl;
{code_indent} }}
{code_indent} {self.gene_return_code()}"""
def gene_kernel_select(self) -> str:
api = self.api
input_names = self.inputs['names']
attrs = self.attrs
kernel = self.kernel
kernel_key_item_init = """
Backend kernel_backend = Backend::UNDEFINED;
DataLayout kernel_layout = DataLayout::PSTRING_UNION;
DataType kernel_data_type = DataType::PSTRING;
"""
# Check the tensor options
attr_backend_count = 0
attr_layout_count = 0
attr_data_type_count = 0
for attr_name in attrs['names']:
if attrs['attr_info'][attr_name][0] == 'Backend':
assert kernel['backend'] is not None, (
f"{api} api: When there is a parameter with 'Backend' type in attributes, you must set backend of kernel manually."
)
attr_backend_count = attr_backend_count + 1
# preprocess kernel configures
kernel_select_code = ""
if kernel['backend'] is not None:
if '>' in kernel['backend']:
vars_list = kernel['backend'].split('>')
assert len(vars_list) == 2, (
f"{api} api: The number of params to set backend with '>' only allows 2, but received {len(vars_list)}."
)
assert (vars_list[0].strip() in attrs['names']) and (
attrs['attr_info'][vars_list[0].strip()][0]
== 'const Place&'
), (
f"{api} api: When use '>' to set kernel backend, the first param should be an attribute with Place type."
)
kernel_select_code = (
kernel_select_code
+ f"""
kernel_backend = ParseBackendWithInputOrder({vars_list[0].strip()}, {vars_list[1].strip()});
"""
)
else:
args_str = ""
for ele in kernel['backend'].split(','):
args_str = args_str + ele.strip() + ', '
kernel_select_code = (
kernel_select_code
+ f"""
kernel_backend = ParseBackend({args_str[:-2]});
"""
)
kernel_select_args = ""
for input_name in input_names:
kernel_select_args = kernel_select_args + input_name + ", "
if len(kernel_select_args) > 2:
kernel_select_args = kernel_select_args[:-2]
kernel_select_code = kernel_key_item_init + kernel_select_code
if len(input_names) > 0:
kernel_select_code = (
kernel_select_code
+ f"""
auto kernel_key_set = ParseKernelKeyByInputArgs({kernel_select_args});
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
kernel_backend = kernel_key.backend();"""
)
return kernel_select_code
def gene_base_api_code(
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
):
api_func_name = self.get_api_func_name()
return f"""
PADDLE_API {self.get_return_type(inplace_flag)} {api_func_name}({self.get_define_args(inplace_flag, grad_flag=grad_flag, append_predefined_out=False)}) {{
{self.gene_kernel_select()}
{self.gen_string_tensor_kernel_code(inplace_flag)}
}}
"""
def header_include():
return """
#include <tuple>
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/utils/optional.h"
"""
def source_include(header_file_path):
return f"""
#include "{header_file_path}"
#include "glog/logging.h"
#include "paddle/common/flags.h"
#include "paddle/phi/api/lib/api_gen_utils.h"
#include "paddle/phi/core/kernel_context.h"
#include "paddle/phi/core/string_tensor.h"
#include "paddle/phi/infermeta/strings/nullary.h"
#include "paddle/phi/infermeta/strings/unary.h"
#include "paddle/phi/api/lib/kernel_dispatch.h"
#include "paddle/phi/core/kernel_registry.h"
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(benchmark);
"""
def api_namespace():
return (
"""
namespace paddle {
namespace experimental {
namespace strings {
""",
"""
} // namespace strings
} // namespace experimental
} // namespace paddle
""",
)
def generate_api(api_yaml_path, header_file_path, source_file_path):
with open(api_yaml_path, 'r') as f:
apis = yaml.load(f, Loader=yaml.FullLoader)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = "paddle/phi/api/include/strings_api.h"
source_file.write(source_include(include_header_file))
source_file.write(namespace[0])
for api in apis:
strings_api = StringsAPI(api)
header_file.write(strings_api.gene_api_declaration())
source_file.write(strings_api.gene_api_code())
header_file.write(namespace[1])
source_file.write(namespace[1])
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ Strings API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to sparse api yaml file',
default='paddle/phi/ops/yaml/strings_ops.yaml',
)
parser.add_argument(
'--api_header_path',
help='output of generated api header code file',
default='paddle/phi/api/include/strings_api.h',
)
parser.add_argument(
'--api_source_path',
help='output of generated api source code file',
default='paddle/phi/api/lib/strings_api.cc',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
header_file_path = options.api_header_path
source_file_path = options.api_source_path
generate_api(api_yaml_path, header_file_path, source_file_path)
if __name__ == '__main__':
main()
@@ -0,0 +1,808 @@
# Copyright (c) 2023 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.
import argparse
import yaml
from api_gen import ForwardAPI
inplace_out_type_map = {
"Tensor": "Tensor&",
"std::vector<Tensor>": "std::vector<Tensor>&",
}
inplace_optional_out_type_map = {
"Tensor": "paddle::optional<Tensor>&",
"std::vector<Tensor>": "paddle::optional<std::vector<Tensor>>&",
}
indent = " "
# E.g.: Prim uses `elementwise_pow + fill_constant` to replace `pow`, so that we use this map to generate the `pow` signature when iterating over `elementwise_pow` API.
specific_ops_map = {"elementwise_pow": "pow"}
operants_base_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#pragma once
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
"""
operants_base_start = """
namespace paddle {
namespace operants {
using Tensor = paddle::Tensor;
using Scalar = paddle::experimental::Scalar;
using IntArray = paddle::experimental::IntArray;
class TensorOperantsBase {
public:
virtual ~TensorOperantsBase() = default;
virtual Tensor add(const Tensor& x, const Scalar& y) = 0;
virtual Tensor divide(const Tensor& x, const Scalar& y) = 0;
virtual Tensor multiply(const Tensor& x, const Scalar& y) = 0;
virtual Tensor subtract(const Tensor& x, const Scalar& y) = 0;
virtual Tensor add(const Scalar& x, const Tensor& y) = 0;
virtual Tensor divide(const Scalar& x, const Tensor& y) = 0;
virtual Tensor multiply(const Scalar& x, const Tensor& y) = 0;
virtual Tensor subtract(const Scalar& x, const Tensor& y) = 0;
virtual Tensor pow(const Tensor& x, const Tensor& y) = 0;
virtual Tensor pow(const Tensor& x, const Scalar& y) = 0;
"""
operants_base_end = """};
} // namespace operants
} // namespace paddle
"""
tensor_api_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/api/include/operants_manager.h"
"""
tensor_api_source_start = """
namespace paddle {
Tensor Tensor::operator+(const Tensor &other) const {
return add(other);
}
Tensor Tensor::operator-(const Tensor &other) const {
return subtract(other);
}
Tensor Tensor::operator*(const Tensor &other) const {
return multiply(other);
}
Tensor Tensor::operator/(const Tensor &other) const {
return divide(other);
}
Tensor Tensor::operator+(const Scalar &other) const {
return add(other);
}
Tensor Tensor::operator-(const Scalar &other) const {
return subtract(other);
}
Tensor Tensor::operator*(const Scalar &other) const {
return multiply(other);
}
Tensor Tensor::operator/(const Scalar &other) const {
return divide(other);
}
Tensor Tensor::add(const Scalar& y) const {
return paddle::OperantsManager::Instance().add(static_cast<const Tensor &>(*this), y);
}
Tensor Tensor::divide(const Scalar& y) const {
return paddle::OperantsManager::Instance().divide(static_cast<const Tensor &>(*this), y);
}
Tensor Tensor::multiply(const Scalar& y) const {
return paddle::OperantsManager::Instance().multiply(static_cast<const Tensor &>(*this), y);
}
Tensor Tensor::subtract(const Scalar& y) const {
return paddle::OperantsManager::Instance().subtract(static_cast<const Tensor &>(*this), y);
}
Tensor Tensor::operator<(const Tensor &other) const {
return less_than(other);
}
Tensor Tensor::operator<=(const Tensor &other) const {
return less_equal(other);
}
Tensor Tensor::operator==(const Tensor &other) const {
return equal(other);
}
Tensor Tensor::operator!=(const Tensor &other) const {
return not_equal(other);
}
Tensor Tensor::operator>(const Tensor &other) const {
return greater_than(other);
}
Tensor Tensor::operator>=(const Tensor &other) const {
return greater_equal(other);
}
Tensor Tensor::operator-() const {
return scale(-1.0, 0.0, true);
}
Tensor Tensor::operator~() const {
return bitwise_not();
}
Tensor Tensor::operator&(const Tensor &other) const {
return bitwise_and(other);
}
Tensor Tensor::operator|(const Tensor &other) const {
return bitwise_or(other);
}
Tensor Tensor::operator^(const Tensor &other) const {
return bitwise_xor(other);
}
Tensor Tensor::pow(const Tensor& y) const {
return paddle::OperantsManager::Instance().pow(static_cast<const Tensor &>(*this), y);
}
Tensor Tensor::pow(const Scalar& y) const {
return paddle::OperantsManager::Instance().pow(static_cast<const Tensor &>(*this), y);
}
PADDLE_API Tensor operator+(const Scalar& x, const Tensor& y) {
return paddle::OperantsManager::Instance().add(x, y);
}
PADDLE_API Tensor operator-(const Scalar& x, const Tensor& y) {
return paddle::OperantsManager::Instance().subtract(x, y);
}
PADDLE_API Tensor operator*(const Scalar& x, const Tensor& y) {
return paddle::OperantsManager::Instance().multiply(x, y);
}
PADDLE_API Tensor operator/(const Scalar& x, const Tensor& y) {
return paddle::OperantsManager::Instance().divide(x, y);
}
"""
tensor_api_source_end = """
} // namespace paddle
"""
operants_header_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#pragma once
#include "paddle/phi/api/include/operants_base.h"
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/common/macros.h"
"""
operants_header_start = """
namespace paddle {
namespace operants {
using Scalar = paddle::experimental::Scalar;
using IntArray = paddle::experimental::IntArray;
class PhiTensorOperants : public TensorOperantsBase {
private:
DISABLE_COPY_AND_ASSIGN(PhiTensorOperants);
public:
PhiTensorOperants() = default;
PADDLE_API Tensor add(const Tensor& x, const Scalar& y);
PADDLE_API Tensor subtract(const Tensor& x, const Scalar& y);
PADDLE_API Tensor multiply(const Tensor& x, const Scalar& y);
PADDLE_API Tensor divide(const Tensor& x, const Scalar& y);
PADDLE_API Tensor add(const Scalar& x, const Tensor& y);
PADDLE_API Tensor subtract(const Scalar& x, const Tensor& y);
PADDLE_API Tensor multiply(const Scalar& x, const Tensor& y);
PADDLE_API Tensor divide(const Scalar& x, const Tensor& y);
PADDLE_API Tensor pow(const Tensor& x, const Tensor& y);
PADDLE_API Tensor pow(const Tensor& x, const Scalar& y);
"""
operants_header_end = """};
} // namespace operants
} // namespace paddle
"""
operants_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#include "paddle/phi/api/include/tensor_operants.h"
#include "paddle/phi/api/include/api.h"
"""
operants_source_start = """
namespace paddle {
namespace operants {
Tensor PhiTensorOperants::add(const Tensor& x, const Scalar& y) {
return paddle::experimental::add(x, paddle::experimental::full_like(x, y));
}
Tensor PhiTensorOperants::subtract(const Tensor& x, const Scalar& y) {
return paddle::experimental::subtract(x, paddle::experimental::full_like(x, y));
}
Tensor PhiTensorOperants::multiply(const Tensor& x, const Scalar& y) {
return paddle::experimental::scale(x, y, 0.0f, true);
}
Tensor PhiTensorOperants::divide(const Tensor& x, const Scalar& y) {
return paddle::experimental::divide(x, paddle::experimental::full_like(x, y));
}
Tensor PhiTensorOperants::add(const Scalar& x, const Tensor& y) {
return paddle::experimental::add(paddle::experimental::full_like(y, x), y);
}
Tensor PhiTensorOperants::subtract(const Scalar& x, const Tensor& y) {
return paddle::experimental::subtract(paddle::experimental::full_like(y, x), y);
}
Tensor PhiTensorOperants::multiply(const Scalar& x, const Tensor& y) {
return paddle::experimental::scale(y, x, 0.0f, true);
}
Tensor PhiTensorOperants::divide(const Scalar& x, const Tensor& y) {
return paddle::experimental::divide(paddle::experimental::full_like(y, x), y);
}
Tensor PhiTensorOperants::pow(const Tensor& x, const Tensor& y) {
return paddle::experimental::elementwise_pow(x, y);
}
Tensor PhiTensorOperants::pow(const Tensor& x, const Scalar& y) {
return paddle::experimental::elementwise_pow(x, paddle::experimental::full_like(x, y));
}
"""
operants_source_end = """
} // namespace operants
} // namespace paddle
"""
operants_manager_header_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#pragma once
#include "paddle/phi/api/include/operants_base.h"
#include "paddle/phi/api/include/tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
#include "paddle/common/macros.h"
#include "paddle/utils/test_macros.h"
"""
operants_manager_header_start = """
namespace paddle {
using Tensor = paddle::Tensor;
using Scalar = paddle::experimental::Scalar;
using IntArray = paddle::experimental::IntArray;
using TensorOperantsBase = paddle::operants::TensorOperantsBase;
/**
* [ Why need OperantsManager? ]
*
* Ideally, overloading tensor operators should call Tensor API directly.
* However, we faced two problems:
*
* 1. Support multiple modes: Tensor operator overloading needs to support
* [static mode / autograd mode / custom operator mode] at the same time.
*
* 2. Decouple phi and fluid: Tensor belongs to the phi library, but it relies
* upon functions in fluid when overloading Tensor operators.
*
* We design OperantsManager to solve these two problems:
*
* 1. use `FLAGS_tensor_operants_mode` to handle overloading mode, set this flag
* at the entry point of each mode:
*
* - FLAGS_tensor_operants_mode = "static": at the construction function of
* `CompositeGradOpMakerBase`.
* - FLAGS_tensor_operants_mode = "eager": at the beginning of dygraph_function.
* - FLAGS_tensor_operants_mode = "phi": at the beginning of the
* `eager_api_run_custom_op` function in eager mode and at the beginning of
* calling kernels in static mode.
*
* In order to guarantee the performance, OperantsManager holds three pointers
* to identify each mode respectively.
*
* 2. Decouple phi with the help of the polymorphism mechanism,
* TensorOperantsBase derives three child classes: PhiTensorOperants,
* EagerTensorOperants, and StaticTensorOperants. We set eager and static tensor
* operants at the fluid library and set phi operants at the phi library.
*
*/
class OperantsManager {
private:
OperantsManager() = default;
DISABLE_COPY_AND_ASSIGN(OperantsManager);
public:
std::unique_ptr<TensorOperantsBase> eager_operants{nullptr};
std::unique_ptr<TensorOperantsBase> static_operants{nullptr};
std::unique_ptr<TensorOperantsBase> phi_operants{nullptr};
public:
PADDLE_API static OperantsManager& Instance();
PADDLE_API Tensor add(const Tensor& x, const Scalar& y);
PADDLE_API Tensor subtract(const Tensor& x, const Scalar& y);
PADDLE_API Tensor multiply(const Tensor& x, const Scalar& y);
PADDLE_API Tensor divide(const Tensor& x, const Scalar& y);
PADDLE_API Tensor add(const Scalar& x, const Tensor& y);
PADDLE_API Tensor subtract(const Scalar& x, const Tensor& y);
PADDLE_API Tensor multiply(const Scalar& x, const Tensor& y);
PADDLE_API Tensor divide(const Scalar& x, const Tensor& y);
PADDLE_API Tensor pow(const Tensor& x, const Tensor& y);
PADDLE_API Tensor pow(const Tensor& x, const Scalar& y);
"""
operants_manager_header_end = """};
} // namespace paddle
"""
operants_manager_source_include = """// Generated by paddle/phi/api/generator/tensor_operants_gen.py
#include "paddle/phi/api/include/operants_manager.h"
#include "glog/logging.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/common/errors.h"
#include "paddle/common/flags.h"
"""
operants_manager_source_start = """
COMMON_DECLARE_string(tensor_operants_mode);
namespace paddle {
OperantsManager& OperantsManager::Instance() {
static OperantsManager g_op_manager;
return g_op_manager;
}
"""
operants_manager_source_end = """
} // namespace paddle
"""
class OperantsAPI(ForwardAPI):
def __init__(self, api_item_yaml, prims=()):
super().__init__(api_item_yaml)
self.is_prim_api = False
if self.get_api_func_name() in prims:
self.is_prim_api = True
def gene_operants_base(self):
api_func_name = self.get_api_func_name()
if api_func_name[-1] != '_':
return f"""
{indent}virtual {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=False)}) = 0;
"""
else:
return f"""
{indent}virtual {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=False)}) = 0;
"""
def get_declare_args_without_first_tensor(self, inplace_flag=False):
func_name = self.get_api_func_name()
declare_args = self.get_input_tensor_args(inplace_flag)
assert len(declare_args) >= 1, (
f"Error! Api {func_name} has no Tensor inputs"
)
first_input_type = " ".join(declare_args[0].split(" ")[:-1])
# NOTE(HongyuJia): Do not consider "const paddle::optional<Tensor>&"
assert first_input_type == "const Tensor&", (
f"Error! The first argument of Tensor Api {func_name} must be Tensor, but received {first_input_type}"
)
for name in self.attrs['names']:
default_value = ''
if self.attrs['attr_info'][name][1] is not None:
default_value = ' = ' + self.attrs['attr_info'][name][1]
declare_args.append(
self.attrs['attr_info'][name][0] + ' ' + name + default_value
)
# remove first Tensor argument
return ", ".join(declare_args[1:])
def get_define_args_without_first_tensor(self, inplace_flag=False):
func_name = self.get_api_func_name()
define_args = self.get_input_tensor_args(inplace_flag)
assert len(define_args) >= 1, (
f"Error! Api {func_name} has no Tensor inputs"
)
first_input_type = " ".join(define_args[0].split(" ")[:-1])
# NOTE(HongyuJia): Do not consider "const paddle::optional<Tensor>&"
assert first_input_type == "const Tensor&", (
f"Error! The first argument of Tensor Api {func_name} must be Tensor, but received {first_input_type}"
)
for name in self.attrs['names']:
define_args.append(self.attrs['attr_info'][name][0] + ' ' + name)
# remove first Tensor argument
return ", ".join(define_args[1:])
def gene_tensor_api_implementation(self):
func_name = self.get_api_func_name()
assert len(self.inputs['names']) >= 1, (
f"Error! Api {func_name} has no Tensor inputs"
)
# remove first Tensor argument
func_args = self.inputs['names'][1:] + self.attrs['names']
if len(func_args) > 0:
func_args_code = ", ".join(["", *func_args])
else:
func_args_code = ""
# func declaration
if func_name[-1] != '_':
return f"""
{self.get_return_type()} Tensor::{func_name}({self.get_define_args_without_first_tensor()}) const {{
{indent}return paddle::OperantsManager::Instance().{func_name}(static_cast<const Tensor &>(*this){func_args_code});
}}
"""
else:
return f"""
{self.get_return_type(inplace_flag=True)} Tensor::{func_name}({self.get_define_args_without_first_tensor(inplace_flag=True)}) const {{
{indent}return paddle::OperantsManager::Instance().{func_name}(static_cast<const Tensor &>(*this){func_args_code});
}}
"""
def gene_operants_declaration(self):
api_func_name = self.get_api_func_name()
if api_func_name[-1] != '_':
return f"""
{indent}PADDLE_API {self.get_return_type()} {api_func_name}({self.get_declare_args(append_predefined_out=False)});
"""
else:
return f"""
{indent}PADDLE_API {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, append_predefined_out=False)});
"""
def gene_operants_implementation(self):
func_name = self.get_api_func_name()
func_args = self.inputs['names'] + self.attrs['names']
func_args_code = ", ".join(func_args)
# func declaration
if func_name[-1] != '_':
return f"""
{self.get_return_type()} PhiTensorOperants::{func_name}({self.get_define_args(append_predefined_out=False)}) {{
{indent}return paddle::experimental::{func_name}({func_args_code});
}}
"""
else:
return f"""
{self.get_return_type(inplace_flag=True)} PhiTensorOperants::{func_name}({self.get_define_args(inplace_flag=True, append_predefined_out=False)}) {{
{indent}return paddle::experimental::{func_name}({func_args_code});
}}
"""
def gene_operants_manager_code(self, is_specific_op=False):
func_name = self.get_api_func_name()
if is_specific_op:
func_name = specific_ops_map[func_name]
func_args = self.inputs['names'] + self.attrs['names']
func_args_code = ", ".join(func_args)
return f"""
if (FLAGS_tensor_operants_mode == "eager") {{
PADDLE_ENFORCE_NE(
this->eager_operants.get(),
nullptr,
common::errors::Unavailable("The eager_operants pointer of "
"OperantsManager is not initialized"));
VLOG(4) << "OperantsManager reusing eager mode API ::{func_name}_ad_func";
return this->eager_operants->{func_name}({func_args_code});
}} else if (FLAGS_tensor_operants_mode == "static") {{
PADDLE_ENFORCE_NE(
this->static_operants.get(),
nullptr,
common::errors::Unavailable("The static_operants pointer of "
"OperantsManager is not initialized"));
VLOG(4) << "OperantsManager reusing static mode API paddle::prim::{func_name}<DescTensor>";
return this->static_operants->{func_name}({func_args_code});
}} else if (FLAGS_tensor_operants_mode == "phi") {{
PADDLE_ENFORCE_NE(
this->phi_operants.get(),
nullptr,
common::errors::Unavailable(
"The phi_operants pointer of OperantsManager is not initialized"));
VLOG(4) << "OperantsManager reusing phi mode API paddle::experimental::{func_name}";
return this->phi_operants->{func_name}({func_args_code});
}} else {{
PADDLE_THROW(common::errors::Unimplemented(
"FLAGS_tensor_operants_mode is not nitialized, please set "
"FLAGS_tensor_operants_mode first, which currently supports eager, "
"phi, and static mode"));
}}
"""
def gene_operants_manager_implementation(self):
func_name = self.get_api_func_name()
final_code = ""
# Codes for arthemetic operants
if func_name in ["add", "subtract", "multiply", "divide"]:
final_code += f"""
{self.get_return_type()} OperantsManager::{func_name}(const Tensor& x, const Scalar& y) {{{self.gene_operants_manager_code()}}}
{self.get_return_type()} OperantsManager::{func_name}(const Scalar& x, const Tensor& y) {{{self.gene_operants_manager_code()}}}
"""
# Codes for specific operants
if func_name in specific_ops_map.keys():
final_code += f"""
{self.get_return_type()} OperantsManager::{specific_ops_map[func_name]}(const Tensor& x, const Tensor& y) {{{self.gene_operants_manager_code(is_specific_op=True)}}}
{self.get_return_type()} OperantsManager::{specific_ops_map[func_name]}(const Tensor& x, const Scalar& y) {{{self.gene_operants_manager_code(is_specific_op=True)}}}
"""
# func declaration
if func_name[-1] != '_':
return (
final_code
+ f"""
{self.get_return_type()} OperantsManager::{func_name}({self.get_define_args(append_predefined_out=False)}) {{{self.gene_operants_manager_code()}}}
"""
)
else:
return (
final_code
+ f"""
{self.get_return_type(inplace_flag=True)} OperantsManager::{func_name}({self.get_define_args(inplace_flag=True, append_predefined_out=False)}) {{
{self.gene_operants_manager_code()}
}}
"""
)
def generate_tensor_operants_api(
api_yaml_path,
operants_base_path,
tensor_api_source_path,
operants_header_path,
operants_source_path,
operants_manager_header_path,
operants_manager_source_path,
tensor_api_yaml_path,
):
apis = []
for each_api_yaml in api_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
apis.extend(api_list)
operants_base_file = open(operants_base_path, 'w')
tensor_api_source_file = open(tensor_api_source_path, 'w')
operants_header_file = open(operants_header_path, 'w')
operants_source_file = open(operants_source_path, 'w')
operants_manager_header_file = open(operants_manager_header_path, 'w')
operants_manager_source_file = open(operants_manager_source_path, 'w')
operants_base_file.write(operants_base_include)
operants_base_file.write(operants_base_start)
tensor_api_source_file.write(tensor_api_source_include)
tensor_api_source_file.write(tensor_api_source_start)
operants_header_file.write(operants_header_include)
operants_header_file.write(operants_header_start)
operants_source_file.write(operants_source_include)
operants_source_file.write(operants_source_start)
operants_manager_header_file.write(operants_manager_header_include)
operants_manager_header_file.write(operants_manager_header_start)
operants_manager_source_file.write(operants_manager_source_include)
operants_manager_source_file.write(operants_manager_source_start)
with open(tensor_api_yaml_path, 'rt') as f:
api_prims = yaml.safe_load(f)
for api in apis:
operants_api = OperantsAPI(api, api_prims)
if operants_api.is_prim_api:
operants_base_file.write(operants_api.gene_operants_base())
tensor_api_source_file.write(
operants_api.gene_tensor_api_implementation()
)
operants_header_file.write(operants_api.gene_operants_declaration())
operants_source_file.write(
operants_api.gene_operants_implementation()
)
operants_manager_header_file.write(
operants_api.gene_operants_declaration()
)
operants_manager_source_file.write(
operants_api.gene_operants_manager_implementation()
)
operants_base_file.write(operants_base_end)
tensor_api_source_file.write(tensor_api_source_end)
operants_header_file.write(operants_header_end)
operants_source_file.write(operants_source_end)
operants_manager_header_file.write(operants_manager_header_end)
operants_manager_source_file.write(operants_manager_source_end)
operants_base_file.close()
tensor_api_source_file.close()
operants_header_file.close()
operants_source_file.close()
operants_manager_header_file.close()
operants_manager_source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to api yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/ops.yaml'],
)
parser.add_argument(
'--operants_base_path',
help='output of generated operants_base header code file',
default='paddle/phi/api/include/operants_base.h',
)
parser.add_argument(
'--tensor_api_source_path',
help='output of generated tensor_api source code file',
default='paddle/phi/api/lib/tensor_api.cc',
)
parser.add_argument(
'--phi_tensor_operants_header_path',
help='output of generated phi_tensor_operants header code file',
default='paddle/phi/api/include/tensor_operants.h',
)
parser.add_argument(
'--phi_tensor_operants_source_path',
help='output of generated phi_tensor_operants source code file',
default='paddle/phi/api/lib/tensor_operants.cc',
)
parser.add_argument(
'--operants_manager_header_path',
help='output of generated operants_manager header code file',
default='paddle/phi/api/include/operants_manager.h',
)
parser.add_argument(
'--operants_manager_source_path',
help='output of generated operants_manager source code file',
default='paddle/phi/api/lib/operants_manager.cc',
)
parser.add_argument(
'--tensor_api_yaml_path',
help='path to tensor_api yaml file',
default='paddle/phi/api/lib/tensor_operants.yaml',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
operants_base_path = options.operants_base_path
tensor_api_source_path = options.tensor_api_source_path
operants_header_path = options.phi_tensor_operants_header_path
operants_source_path = options.phi_tensor_operants_source_path
operants_manager_header_path = options.operants_manager_header_path
operants_manager_source_path = options.operants_manager_source_path
tensor_api_yaml_path = options.tensor_api_yaml_path
generate_tensor_operants_api(
api_yaml_path,
operants_base_path,
tensor_api_source_path,
operants_header_path,
operants_source_path,
operants_manager_header_path,
operants_manager_source_path,
tensor_api_yaml_path,
)
if __name__ == '__main__':
main()
@@ -0,0 +1,210 @@
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import yaml
from api_gen import ForwardAPI
kernel_func_set = set()
def get_wrapped_infermeta_name(api_name):
return api_name.capitalize() + 'InferMeta'
def gene_wrapped_infermeta_and_register(api):
if api.is_base_api and not api.is_dygraph_api:
register_code = f"""
PD_REGISTER_INFER_META_FN({api.kernel['func'][0]}, phi::{api.infer_meta['func']});"""
if api.infer_meta['param'] is not None:
if api.kernel['func'][0] in kernel_func_set:
return '', '', ''
kernel_params = api.kernel['param']
if kernel_params is None:
kernel_params = api.inputs['names'] + api.attrs['names']
if kernel_params == api.infer_meta['param']:
return '', '', register_code
assert len(api.infer_meta['param']) <= len(kernel_params), (
f"{api.api} api: Parameters error. The params of infer_meta should be a subset of kernel params."
)
tensor_type_map = {
'const Tensor&': 'const MetaTensor&',
'const std::vector<Tensor>&': 'const std::vector<const MetaTensor*>&',
'Tensor': 'MetaTensor*',
'std::vector<Tensor>': 'std::vector<MetaTensor*>',
'const paddle::optional<Tensor>&': 'const MetaTensor&',
}
wrapped_infermeta_name = get_wrapped_infermeta_name(
api.kernel['func'][0]
)
args = []
for input_name in api.inputs['names']:
if input_name in kernel_params:
args.append(
tensor_type_map[api.inputs['input_info'][input_name]]
+ ' '
+ input_name
)
for attr_name in api.attrs['names']:
if attr_name in kernel_params:
args.append(
api.attrs['attr_info'][attr_name][0] + ' ' + attr_name
)
for i, out_type in enumerate(api.outputs['types']):
args.append(
tensor_type_map[out_type] + ' ' + api.outputs['names'][i]
)
invoke_param = api.infer_meta['param']
invoke_param.extend(api.outputs['names'])
declare_code = f"""
void {wrapped_infermeta_name}({", ".join(args)});
"""
defined_code = f"""
void {wrapped_infermeta_name}({", ".join(args)}) {{
{api.infer_meta['func']}({", ".join(invoke_param)});
}}
"""
register_code = f"""
PD_REGISTER_INFER_META_FN({api.kernel['func'][0]}, phi::{get_wrapped_infermeta_name(api.kernel['func'][0])});"""
kernel_func_set.add(api.kernel['func'][0])
return declare_code, defined_code, register_code
else:
return '', '', register_code
else:
return '', '', ''
def header_include():
return """
#include "paddle/phi/core/meta_tensor.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/phi/common/int_array.h"
"""
def source_include(header_file_path):
return f"""
#include "{header_file_path}"
#include "paddle/phi/core/infermeta_utils.h"
#include "paddle/phi/infermeta/binary.h"
#include "paddle/phi/infermeta/multiary.h"
#include "paddle/phi/infermeta/nullary.h"
#include "paddle/phi/infermeta/unary.h"
#include "paddle/phi/infermeta/ternary.h"
"""
def api_namespace():
return (
"""
namespace phi {
""",
"""
} // namespace phi
""",
)
def generate_wrapped_infermeta_and_register(
api_yaml_path, header_file_path, source_file_path
):
apis = []
for each_api_yaml in api_yaml_path:
with open(each_api_yaml, 'r') as f:
api_list = yaml.load(f, Loader=yaml.FullLoader)
if api_list:
apis.extend(api_list)
header_file = open(header_file_path, 'w')
source_file = open(source_file_path, 'w')
namespace = api_namespace()
header_file.write("#pragma once\n")
header_file.write(header_include())
header_file.write(namespace[0])
include_header_file = "paddle/phi/infermeta/generated.h"
source_file.write(source_include(include_header_file))
source_file.write(namespace[0])
infermeta_register_code = ''
for api in apis:
api_item = ForwardAPI(api)
(
declare_code,
defined_code,
register_code,
) = gene_wrapped_infermeta_and_register(api_item)
header_file.write(declare_code)
source_file.write(defined_code)
if infermeta_register_code.find(register_code) == -1:
infermeta_register_code = infermeta_register_code + register_code
header_file.write(namespace[1])
source_file.write(namespace[1])
source_file.write(infermeta_register_code)
header_file.close()
source_file.close()
def main():
parser = argparse.ArgumentParser(
description='Generate PaddlePaddle C++ API files'
)
parser.add_argument(
'--api_yaml_path',
help='path to api yaml file',
nargs='+',
default=['paddle/phi/ops/yaml/ops.yaml'],
)
parser.add_argument(
'--wrapped_infermeta_header_path',
help='output of generated wrapped_infermeta header code file',
default='paddle/phi/infermeta/generated.h',
)
parser.add_argument(
'--wrapped_infermeta_source_path',
help='output of generated wrapped_infermeta source code file',
default='paddle/phi/infermeta/generated.cc',
)
options = parser.parse_args()
api_yaml_path = options.api_yaml_path
header_file_path = options.wrapped_infermeta_header_path
source_file_path = options.wrapped_infermeta_source_path
generate_wrapped_infermeta_and_register(
api_yaml_path, header_file_path, source_file_path
)
if __name__ == '__main__':
main()