Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Code Generation for operators, op makers and argument mapping functions #41772

Merged
merged 39 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
468f530
WIP: auto generate op and kernel signature
Mar 18, 2022
fb475f1
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Mar 21, 2022
6f58120
WIP: add diag_block and update api_gen process
Mar 21, 2022
520eb03
resolve conflicts
Mar 28, 2022
2a4e1b2
update for explicit typed scalar
Mar 28, 2022
bbea234
fix for backward api opmaker
Mar 30, 2022
a322c6f
delete templates for c++ api
Mar 30, 2022
fb496ec
fix initializer list as default value for vectors
Mar 30, 2022
8263219
add type annotation for attributes in comment
Mar 30, 2022
73d20cd
stop explicitly construct a vector for default values
Mar 30, 2022
526aef5
fix for backward op maker when it has Scalar or ScalarArray attributes
Mar 30, 2022
824bbcd
resolve conflicts
Mar 30, 2022
8b37979
remove codegen of ops in cmakelists.txt
Mar 30, 2022
40c6230
restore codegen files
Mar 31, 2022
b37990d
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Mar 31, 2022
4bf3fda
update
Mar 31, 2022
88197e7
fix for output code in opmaker
Apr 1, 2022
36a57a4
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Apr 1, 2022
ee307c9
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Apr 6, 2022
a814643
fix templates and add strict validation, fix several bugs in api yaml
Apr 6, 2022
eba7ef2
move to_named_dict to parse_utils.py
Apr 6, 2022
8d5e827
fix namespace of GradVarName, add processing for intermediate and no_…
Apr 7, 2022
a04c803
add argument type checking for forward_call and forward_api
Apr 7, 2022
cd8933d
add correct processing of inplace no_need_buffer and intermediate out…
Apr 7, 2022
f176a53
fix GradVarName, paddle::framework and phi each has its own
Apr 7, 2022
8f6bb02
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Apr 7, 2022
c7d5b6c
fix processing for Scalar
Apr 8, 2022
f163be5
fix default value for string, no need to wrap them with quotation marks
Apr 12, 2022
6e01fb3
merge upstream and add support for optional size expression for outpu…
Apr 12, 2022
457a49f
integration into build system
Apr 12, 2022
dd3c77f
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
Apr 13, 2022
c66916f
add process to install pyyaml and jinja2
Apr 13, 2022
1af9f0a
remove uncessary bash script
Apr 13, 2022
b20d4b9
move cmake commands to phi/api/lib, and other fixes
Apr 14, 2022
1b0720a
fix typos
Apr 18, 2022
5a112e1
add linear and linear_grad kernel
Apr 18, 2022
12a788d
Merge branch 'develop' of github.com:PaddlePaddle/Paddle into opmaker
May 9, 2022
761be90
update operator generation code
May 10, 2022
6bf8db3
merge upstream and resolve conflicts
May 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set(api_yaml_path "${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/api.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/sparse_api.yaml")
set(backward_yaml_path "${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/backward.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/sparse_bw_api.yaml")
set(api_yaml_path "${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/api.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/new_api.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/sparse_api.yaml")
set(backward_yaml_path "${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/backward.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/new_backward.yaml,${PADDLE_SOURCE_DIR}/python/paddle/utils/code_gen/sparse_bw_api.yaml")
set(tmp_forwards_cc_path "${PADDLE_SOURCE_DIR}/paddle/fluid/eager/api/generated/eager_generated/forwards/tmp_dygraph_functions.cc")
set(tmp_forwards_h_path "${PADDLE_SOURCE_DIR}/paddle/fluid/eager/api/generated/eager_generated/forwards/tmp_dygraph_functions.h")
set(tmp_nodes_cc_path "${PADDLE_SOURCE_DIR}/paddle/fluid/eager/api/generated/eager_generated/backwards/tmp_nodes.cc")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,24 @@ def AssertMessage(lhs_str, rhs_str):

def ReadFwdFile(filepath):
f = open(filepath, 'r')
# empty file loaded by yaml is None
contents = yaml.load(f, Loader=yaml.FullLoader)
f.close()
return contents
return contents if contents is not None else []


def ReadBwdFile(filepath):
f = open(filepath, 'r')
contents = yaml.load(f, Loader=yaml.FullLoader)
ret = {}
for content in contents:
assert 'backward_api' in content.keys(), AssertMessage('backward_api',
content.keys())
if 'backward_api' in content.keys():
api_name = content['backward_api']

ret[api_name] = content
if contents is not None:
for content in contents:
assert 'backward_api' in content.keys(), AssertMessage(
'backward_api', content.keys())
if 'backward_api' in content.keys():
api_name = content['backward_api']

ret[api_name] = content
f.close()
return ret

Expand Down Expand Up @@ -207,6 +209,8 @@ def ParseYamlArgs(string):

assert arg_type in yaml_types_mapping.keys(
), f"The argument type {arg_type} in yaml config is not supported in yaml_types_mapping."
if arg_type in ["DataType", "DataLayout"] and default_value is not None:
default_value = f"paddle::experimental::{default_value}"
arg_type = yaml_types_mapping[arg_type]

arg_name = RemoveSpecialSymbolsInName(arg_name)
Expand Down
105 changes: 100 additions & 5 deletions paddle/phi/api/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(api_gen_base ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/api_base.py)
# forward api file
set(api_gen_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/api_gen.py)
set(api_yaml_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/api.yaml)
set(new_api_yaml_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/new_api.yaml)
set(api_header_file ${CMAKE_SOURCE_DIR}/paddle/phi/api/include/api.h)
set(api_source_file ${CMAKE_SOURCE_DIR}/paddle/phi/api/lib/api.cc)
set(api_header_file_tmp ${api_header_file}.tmp)
Expand All @@ -21,6 +22,7 @@ set(api_source_file_tmp ${api_source_file}.tmp)
# backward api file
set(bw_api_gen_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/backward_api_gen.py)
set(bw_api_yaml_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/backward.yaml)
set(new_bw_api_yaml_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/new_backward.yaml)
set(bw_api_header_file ${CMAKE_SOURCE_DIR}/paddle/phi/api/backward/backward_api.h)
set(bw_api_source_file ${CMAKE_SOURCE_DIR}/paddle/phi/api/lib/backward_api.cc)
set(bw_api_header_file_tmp ${bw_api_header_file}.tmp)
Expand Down Expand Up @@ -59,20 +61,113 @@ set(strings_api_source_file_tmp ${strings_api_source_file}.tmp)

# wrapped infermeta file
set(wrapped_infermeta_gen_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/wrapped_infermeta_gen.py)
set(api_yaml_file ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/api.yaml)
set(wrapped_infermeta_header_file ${CMAKE_SOURCE_DIR}/paddle/phi/infermeta/generated.h)
set(wrapped_infermeta_source_file ${CMAKE_SOURCE_DIR}/paddle/phi/infermeta/generated.cc)

if (NOT PYTHON_EXECUTABLE)
find_package(PythonInterp REQUIRED)
endif()

# install extra dependencies
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -m pip install -U pyyaml jinja2
)

# parse apis
set(parsed_api_dir ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen/parsed_apis)
set(generated_op_path ${CMAKE_SOURCE_DIR}/paddle/fluid/operators/generated_op.cc)
set(generated_argument_mapping_path ${CMAKE_SOURCE_DIR}/paddle/phi/ops/compat/generated_sig.cc)
message("parse api yamls:
- ${api_yaml_file}
- ${new_api_yaml_file}
- ${bw_api_yaml_file}
- ${new_bw_api_yaml_file}")
execute_process(
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen
COMMAND ${CMAKE_COMMAND} -E make_directory ${parsed_api_dir}
COMMAND ${PYTHON_EXECUTABLE} parse_api.py
--api_yaml_path ./api.yaml
--output_path ./parsed_apis/api.parsed.yaml
COMMAND ${PYTHON_EXECUTABLE} parse_api.py
--api_yaml_path ./new_api.yaml
--output_path ./parsed_apis/new_api.parsed.yaml
COMMAND ${PYTHON_EXECUTABLE} parse_api.py
--api_yaml_path ./backward.yaml
--output_path ./parsed_apis/backward_api.parsed.yaml
--backward
COMMAND ${PYTHON_EXECUTABLE} parse_api.py
--api_yaml_path ./new_backward.yaml
--output_path ./parsed_apis/new_backward_api.parsed.yaml
--backward
RESULTS_VARIABLE _results
)
foreach(_result in ${_results})
if (${_result})
message(FATAL_ERROR "api yaml parsing failed, exiting.")
endif()
endforeach()

# validation of api yamls
message("validate api yaml:
- ${parsed_api_dir}/new_api.parsed.yaml
- ${parsed_api_dir}/new_backward_api.parsed.yaml")
execute_process(
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen
COMMAND ${PYTHON_EXECUTABLE} cross_validate.py
--forward_yaml_paths ./parsed_apis/api.parsed.yaml ./parsed_apis/new_api.parsed.yaml
--backward_yaml_paths ./parsed_apis/backward_api.parsed.yaml ./parsed_apis/new_backward_api.parsed.yaml
RESULT_VARIABLE _result
)
if (${_result})
message(FATAL_ERROR "api validation failed, exiting." )
endif()

# code generation for op, op makers, and argument mapping functions
message("create or remove auto-geneated operators: ${generated_op_path}.tmp
create or remove auto-geneated argument mappings: ${generated_argument_mapping_path}.tmp")
execute_process(
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python/paddle/utils/code_gen
COMMAND ${PYTHON_EXECUTABLE} generate_op.py
--api_yaml_path ./parsed_apis/new_api.parsed.yaml
--backward_api_yaml_path ./parsed_apis/new_backward_api.parsed.yaml
--output_op_path "${generated_op_path}.tmp"
--output_arg_map_path "${generated_argument_mapping_path}.tmp"
RESULT_VARIABLE _result
)
if (${_result})
message(FATAL_ERROR "operator codegen failed, exiting." )
endif()


if(EXISTS "${generated_op_path}.tmp" AND EXISTS "${generated_op_path}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${generated_op_path}.tmp" "${generated_op_path}")
message("copy if different ${generated_op_path}.tmp ${generated_op_path}")
elseif(EXISTS "${generated_op_path}.tmp")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${generated_op_path}.tmp" "${generated_op_path}")
message("copy ${generated_op_path}.tmp ${generated_op_path}")
else()
execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f "${generated_op_path}")
message("remove ${generated_op_path}")
endif()


if(EXISTS "${generated_argument_mapping_path}.tmp" AND EXISTS "${generated_argument_mapping_path}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${generated_argument_mapping_path}.tmp" "${generated_argument_mapping_path}")
message("copy if different ${generated_argument_mapping_path}.tmp ${generated_argument_mapping_path}")
elseif(EXISTS "${generated_argument_mapping_path}.tmp")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${generated_argument_mapping_path}.tmp" "${generated_argument_mapping_path}")
message("copy ${generated_argument_mapping_path}.tmp ${generated_argument_mapping_path}")
else()
execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f "${generated_argument_mapping_path}")
message("remove ${generated_argument_mapping_path}")
endif()

# generate forward api
add_custom_command(
OUTPUT ${api_header_file} ${api_source_file}
COMMAND ${PYTHON_EXECUTABLE} -m pip install pyyaml
COMMAND ${PYTHON_EXECUTABLE} ${api_gen_file}
--api_yaml_path ${api_yaml_file}
--api_yaml_path ${api_yaml_file} ${new_api_yaml_file}
--api_header_path ${api_header_file_tmp}
--api_header_path ${api_header_file_tmp}
--api_source_path ${api_source_file_tmp}
Expand All @@ -86,7 +181,7 @@ add_custom_command(
add_custom_command(
OUTPUT ${bw_api_header_file} ${bw_api_source_file} ${bw_api_header_file_tmp} ${bw_api_source_file_tmp}
COMMAND ${PYTHON_EXECUTABLE} ${bw_api_gen_file}
--backward_yaml_path ${bw_api_yaml_file}
--backward_yaml_path ${bw_api_yaml_file} ${new_bw_api_yaml_file}
--backward_header_path ${bw_api_header_file_tmp}
--backward_source_path ${bw_api_source_file_tmp}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${bw_api_header_file_tmp} ${bw_api_header_file}
Expand Down Expand Up @@ -138,7 +233,7 @@ add_custom_command(
add_custom_command(
OUTPUT ${dygraph_api_header_file} ${dygraph_api_source_file}
COMMAND ${PYTHON_EXECUTABLE} ${im_api_gen_file}
--api_yaml_path ${api_yaml_file}
--api_yaml_path ${api_yaml_file} ${new_api_yaml_file}
--sparse_api_yaml_path ${sparse_api_yaml_file}
--dygraph_api_header_path ${dygraph_api_header_file_tmp}
--dygraph_api_source_path ${dygraph_api_source_file_tmp}
Expand All @@ -151,7 +246,7 @@ add_custom_command(
add_custom_command(
OUTPUT ${wrapped_infermeta_header_file} ${wrapped_infermeta_source_file}
COMMAND ${PYTHON_EXECUTABLE} ${wrapped_infermeta_gen_file}
--api_yaml_path ${api_yaml_file}
--api_yaml_path ${api_yaml_file} ${new_api_yaml_file}
--wrapped_infermeta_header_path ${wrapped_infermeta_header_file}
--wrapped_infermeta_source_path ${wrapped_infermeta_source_file}
DEPENDS ${api_yaml_file} ${wrapped_infermeta_gen_file} ${api_gen_base}
Expand Down
1 change: 1 addition & 0 deletions paddle/phi/common/scalar.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ template <typename T>
class ScalarBase {
public:
// Constructor support implicit
ScalarBase() : ScalarBase(0) {}
ScalarBase(double val) : dtype_(DataType::FLOAT64) { // NOLINT
data_.f64 = val;
}
Expand Down
4 changes: 2 additions & 2 deletions python/paddle/utils/code_gen/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@
skip_transform : x

- api : gather
args : (Tensor x, Tensor index, Scalar axis=0)
args : (Tensor x, Tensor index, Scalar(int) axis=0)
output : Tensor(out)
infer_meta :
func : GatherInferMeta
Expand Down Expand Up @@ -2020,7 +2020,7 @@
backward : subtract_grad

- api : sum
args : (Tensor x, int64_t[] dims={}, DataType out_dtype=paddle::experimental::DataType::UNDEFINED, bool keep_dim=false)
args : (Tensor x, int64_t[] dims={}, DataType out_dtype=DataType::UNDEFINED, bool keep_dim=false)
output : Tensor(out)
infer_meta :
func : SumInferMeta
Expand Down
10 changes: 8 additions & 2 deletions python/paddle/utils/code_gen/api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,14 @@ def api_namespace():


def generate_api(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)

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')

Expand Down Expand Up @@ -259,6 +264,7 @@ def main():
parser.add_argument(
'--api_yaml_path',
help='path to api yaml file',
nargs='+',
default='python/paddle/utils/code_gen/api.yaml')

parser.add_argument(
Expand Down
9 changes: 2 additions & 7 deletions python/paddle/utils/code_gen/backward.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@
param : [grad_x_grad, axis]
kernel :
func : concat
no_need_buffer : x

- backward_api : concat_grad
forward : concat (Tensor[] x, Scalar axis) -> Tensor(out)
Expand Down Expand Up @@ -507,7 +506,6 @@
param : [out_grad]
kernel :
func : dropout_grad
optional : seed_tensor

- backward_api : eigh_grad
forward : eigh (Tensor x, str uplo) -> Tensor(out_w), Tensor(out_v)
Expand Down Expand Up @@ -648,7 +646,6 @@
data_type: out_grad
backend: out_grad
layout: out_grad
no_need_buffer : x

- backward_api : flip_grad
forward : flip (Tensor x, int[] axis) -> Tensor(out)
Expand Down Expand Up @@ -866,7 +863,6 @@
param : [out_grad]
kernel :
func : label_smooth_grad
optional : prior_dist

- backward_api : layer_norm_grad
forward : layer_norm (Tensor x, Tensor scale, Tensor bias, float epsilon, int begin_norm_axis, bool is_test) -> Tensor(out), Tensor(mean), Tensor(variance)
Expand Down Expand Up @@ -1483,7 +1479,7 @@
no_need_buffer : grad_out

- backward_api : reshape_grad
forward : reshape_with_xshape (Tensor x, IntArray shape) -> Tensor(out), Tensor(xshape)
forward : reshape (Tensor x, IntArray shape) -> Tensor(out), Tensor(xshape)
args : (Tensor xshape, Tensor out_grad)
output : Tensor(x_grad)
infer_meta :
Expand Down Expand Up @@ -1814,7 +1810,7 @@
backward : sum_triple_grad

- backward_api : sum_grad
forward : sum (Tensor x, int64_t[] dims={}, DataType out_dtype=paddle::experimental::DataType::UNDEFINED, bool keep_dim=false) -> Tensor(out)
forward : sum (Tensor x, int64_t[] dims={}, DataType out_dtype=DataType::UNDEFINED, bool keep_dim=false) -> Tensor(out)
args : (Tensor x, Tensor out_grad, int64_t[] dims, bool keep_dim, bool reduce_all=false)
output : Tensor(x_grad)
infer_meta :
Expand All @@ -1830,7 +1826,6 @@
args : (Tensor grad_grad_x, Tensor grad_grad_out_grad, int64_t[] dims={}, bool keep_dim=false, bool reduce_all=false)
output : Tensor(grad_grad_x_grad)
invoke : sum_grad(grad_grad_x, grad_grad_out_grad, dims, keep_dim, reduce_all, grad_grad_x_grad)
no_need_buffer : x

- backward_api : swish_grad
forward : swish (Tensor x, float beta=1.0) -> Tensor(out)
Expand Down
10 changes: 8 additions & 2 deletions python/paddle/utils/code_gen/backward_api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,13 @@ def backward_api_namespace():
def generate_backward_api(backward_yaml_path, header_file_path,
source_file_path):

with open(backward_yaml_path, 'r') as f:
bw_apis = yaml.load(f, Loader=yaml.FullLoader)
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')

Expand Down Expand Up @@ -270,6 +275,7 @@ def main():
parser.add_argument(
'--backward_yaml_path',
help='path to backward yaml file',
nargs='+',
default='python/paddle/utils/code_gen/backward.yaml')
parser.add_argument(
'--backward_header_path',
Expand Down
52 changes: 52 additions & 0 deletions python/paddle/utils/code_gen/cross_validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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
from itertools import chain
from pathlib import Path

import yaml
from parse_utils import cross_validate, to_named_dict


def main(forward_api_yaml_paths, backward_api_yaml_paths):
apis = {}
for api_yaml_path in chain(forward_api_yaml_paths, backward_api_yaml_paths):
with open(api_yaml_path, "rt", encoding="utf-8") as f:
api_list = yaml.safe_load(f)
if api_list is not None:
apis.update(to_named_dict((api_list)))

cross_validate(apis)


if __name__ == "__main__":
current_dir = Path(__file__).parent / "temp"
parser = argparse.ArgumentParser(
description="Parse api yaml into canonical format.")
parser.add_argument(
'--forward_yaml_paths',
type=str,
nargs='+',
default=str(current_dir / "api.parsed.yaml"),
help="forward api yaml file.")
parser.add_argument(
'--backward_yaml_paths',
type=str,
nargs='+',
default=str(current_dir / "backward.yaml.yaml"),
help="backward api yaml file.")

args = parser.parse_args()
main(args.forward_yaml_paths, args.backward_yaml_paths)
Loading