chore: import upstream snapshot with attribution
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
Reference in New Issue
Block a user