Files
2026-07-13 12:40:42 +08:00

1822 lines
61 KiB
Python
Executable File

# 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 collections
import logging
import os
import warnings
from functools import reduce
from paddle.base.framework import generate_control_dev_var_name
from paddle.distributed.io import is_persistable
from paddle.framework import core
# logging.basicConfig(
# format='%(levelname)s - %(asctime)s - %(pathname)s: %(lineno)s - %(message)s', level=logging.INFO)
# logger = logging.getLogger(__name__)
OP_NAME_SCOPE = "op_namescope"
CLIP_OP_NAME_SCOPE = "gradient_clip"
STEP_COUNTER = "@PS_STEP_COUNTER@"
LEARNING_RATE_DECAY_COUNTER = "@LR_DECAY_COUNTER@"
OP_ROLE_VAR_ATTR_NAME = core.op_proto_and_checker_maker.kOpRoleVarAttrName()
RPC_OP_ROLE_ATTR_NAME = core.op_proto_and_checker_maker.kOpRoleAttrName()
RPC_OP_ROLE_ATTR_VALUE = core.op_proto_and_checker_maker.OpRole.RPC
op_role = core.op_proto_and_checker_maker.OpRole
op_role_attr_name = core.op_proto_and_checker_maker.kOpRoleAttrName()
LR_SCHED_OP_ROLE_ATTR_VALUE = core.op_proto_and_checker_maker.OpRole.LRSched
OPT_OP_ROLE_ATTR_VALUE = core.op_proto_and_checker_maker.OpRole.Optimize
backward = core.op_proto_and_checker_maker.OpRole.Backward
OP_DEVICE_KEY = core.op_proto_and_checker_maker.kOpDeviceAttrName()
DEVICE_LIST = ["cpu", "gpu", "xpu"]
COMMUNICATE_OPS_TYPE = ["send", "recv", "fetch_barrier", "send_barrier"]
SPARSE_OP_LIST = ["lookup_table", "lookup_table_v2"]
SPARSE_OP_TYPE_DICT = {"lookup_table": "W", "lookup_table_v2": "W"}
SPARSE_GRAD_OP_TYPE_DICT = {
"lookup_table_grad": "W",
"lookup_table_v2_grad": "W",
}
DEFAULT_DEVICE = 'cpu'
DATA_NORM_NAME = [".batch_size", ".batch_sum", ".batch_square_sum"]
DATA_NORM_GRAD_NAME = [x + "@GRAD" for x in DATA_NORM_NAME]
def logger_config(log_path, logging_name):
logger = logging.getLogger(logging_name)
logger.setLevel(level=logging.WARNING)
handler = logging.FileHandler(
log_path, mode='a', encoding='UTF-8', delay=True
)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(
'%(levelname)s - %(asctime)s - %(pathname)s: %(lineno)s - %(message)s'
)
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.addHandler(console)
return logger
ps_log_root_dir = './ps_log/'
logger = logger_config(
log_path='./ps_usr_print_log', logging_name='ps_usr_print_log'
)
class DistributedMode:
SYNC = 0
ASYNC = 1
HALF_ASYNC = 2
GEO = 3
FL = 4
NU = 5
class TrainerRuntimeConfig:
def __init__(self, valid_strategy):
self.mode = None
num_threads = os.getenv("CPU_NUM", "1")
send_queue_size = num_threads
k_steps = valid_strategy.a_sync_configs["k_steps"]
if not valid_strategy.a_sync and k_steps == 0:
self.mode = DistributedMode.SYNC
if valid_strategy.a_sync and k_steps == 0:
self.mode = DistributedMode.ASYNC
if valid_strategy.a_sync and k_steps > 0:
self.mode = DistributedMode.GEO
send_queue_size = k_steps
self.runtime_configs = {}
self.runtime_configs['communicator_max_merge_var_num'] = os.getenv(
"FLAGS_communicator_max_merge_var_num", send_queue_size
)
self.runtime_configs['communicator_send_queue_size'] = os.getenv(
"FLAGS_communicator_send_queue_size", send_queue_size
)
self.runtime_configs['communicator_independent_recv_thread'] = (
os.getenv("FLAGS_communicator_independent_recv_thread", "1")
)
self.runtime_configs['communicator_min_send_grad_num_before_recv'] = (
os.getenv(
"FLAGS_communicator_min_send_grad_num_before_recv", num_threads
)
)
self.runtime_configs['communicator_thread_pool_size'] = os.getenv(
"FLAGS_communicator_thread_pool_size", "5"
)
self.runtime_configs['communicator_send_wait_times'] = os.getenv(
"FLAGS_communicator_send_wait_times", "5"
)
self.runtime_configs['communicator_is_sgd_optimizer'] = os.getenv(
"FLAGS_communicator_is_sgd_optimizer", "1"
)
def get_communicator_flags(self):
need_keys = []
num_threads = os.getenv("CPU_NUM", "1")
mode_str = ""
if self.mode is None or self.mode == DistributedMode.ASYNC:
need_keys = self.runtime_configs.keys()
mode_str = "async"
elif (
self.mode == DistributedMode.SYNC
or self.mode == DistributedMode.HALF_ASYNC
):
mode_str = "sync or half_async"
need_keys = [
'communicator_max_merge_var_num',
'communicator_send_wait_times',
'communicator_thread_pool_size',
'communicator_send_queue_size',
]
elif self.mode == DistributedMode.GEO:
mode_str = "GEO"
need_keys = [
'communicator_thread_pool_size',
'communicator_send_wait_times',
'communicator_max_merge_var_num',
'communicator_send_queue_size',
]
else:
raise ValueError("Unsupported Mode")
if (
self.mode == DistributedMode.SYNC
or self.mode == DistributedMode.HALF_ASYNC
):
max_merge_var_num = self.runtime_configs[
'communicator_max_merge_var_num'
]
send_queue_size = self.runtime_configs[
'communicator_send_queue_size'
]
if max_merge_var_num != num_threads:
print(
f'WARNING: In {mode_str} mode, communicator_max_merge_var_num '
'must be equal to CPU_NUM. But received, '
f'communicator_max_merge_var_num = {max_merge_var_num}, CPU_NUM = '
f'{num_threads}. communicator_max_merge_var_num will be forced to {num_threads}.'
)
self.runtime_configs['communicator_max_merge_var_num'] = (
num_threads
)
if send_queue_size != num_threads:
print(
f'WARNING: In {mode_str} mode, communicator_send_queue_size '
'must be equal to CPU_NUM. But received, '
f'communicator_send_queue_size = {send_queue_size}, CPU_NUM = '
f'{num_threads}. communicator_send_queue_size will be forced to {num_threads}.'
)
self.runtime_configs['communicator_send_queue_size'] = (
num_threads
)
return {key: str(self.runtime_configs[key]) for key in need_keys}
def get_lr_ops(program):
lr_ops = []
for index, op in enumerate(program.global_block().ops):
role_id = int(op.attr(RPC_OP_ROLE_ATTR_NAME))
if role_id == int(LR_SCHED_OP_ROLE_ATTR_VALUE) or role_id == int(
LR_SCHED_OP_ROLE_ATTR_VALUE
) | int(OPT_OP_ROLE_ATTR_VALUE):
lr_ops.append(op)
return lr_ops
def get_optimize_ops(_program, remote_sparse=[]):
block = _program.global_block()
opt_ops = []
for op in block.ops:
if _is_opt_role_op(op):
if (
len(remote_sparse) > 0
and op.input("Param")[0] not in remote_sparse
): # for fl: only delete remote sparse optimize
continue
# delete clip op from opt_ops when run in Parameter Server mode
if (
OP_NAME_SCOPE in op.all_attrs()
and CLIP_OP_NAME_SCOPE in op.attr(OP_NAME_SCOPE)
):
op._set_attr(
"op_role",
int(core.op_proto_and_checker_maker.OpRole.Backward),
)
continue
opt_ops.append(op)
return opt_ops
def get_datanorm_ops(_program):
block = _program.global_block()
opt_ops = []
for op in block.ops:
if op.type == 'data_norm':
opt_ops.append(op)
return opt_ops
def get_dist_env():
trainer_id = int(os.getenv('PADDLE_TRAINER_ID', '0'))
trainer_endpoints = ''
current_endpoint = ''
num_trainers = 0
if os.getenv('PADDLE_TRAINER_ENDPOINTS'):
trainer_endpoints = os.getenv('PADDLE_TRAINER_ENDPOINTS')
current_endpoint = trainer_endpoints.split(',')[trainer_id]
num_trainers = len(trainer_endpoints.split(','))
return {
'trainer_id': trainer_id,
'num_trainers': num_trainers,
'current_endpoint': current_endpoint,
'trainer_endpoints': trainer_endpoints,
}
def get_role_id(role_maker):
try:
return role_maker._role_id()
except Exception:
return role_maker.role_id()
def get_ps_endpoint(role_maker):
try:
return role_maker._get_pserver_endpoints()[get_role_id(role_maker)]
except Exception:
return role_maker.get_pserver_endpoints()[get_role_id(role_maker)]
def get_ps_endpoints(role_maker):
try:
return role_maker._get_pserver_endpoints()
except Exception:
return role_maker.get_pserver_endpoints()
def get_heter_worker_endpoint(role_maker):
return role_maker._get_heter_worker_endpoint()
def get_trainer_endpoint(role_maker):
return role_maker._get_trainer_endpoint()
def get_trainer_endpoints(role_maker):
return role_maker._get_trainer_endpoints()
def get_previous_stage_trainers(role_maker):
try:
return role_maker._get_previous_trainers()
except Exception:
return role_maker.get_previous_trainers()
def is_distributed_sparse_op(op):
if op.type in SPARSE_OP_LIST and op.attr('is_distributed') is True:
return True
if (
op.type == "distributed_lookup_table"
and op.attr('is_distributed') is True
):
return True
return False
def get_sparse_tablename(op):
return op.input("W")[0]
def is_sparse_op(op):
if (
op.type in SPARSE_OP_LIST
and op.attr('is_sparse') is True
and op.attr('is_distributed') is False
):
return True
if (
op.type == "distributed_lookup_table"
and op.attr('is_distributed') is False
):
return True
return False
def get_sparse_tablenames(programs, is_distributed):
tablenames = set()
for program in programs:
if is_distributed:
for op in program.global_block().ops:
if is_distributed_sparse_op(op):
tablenames.add(get_sparse_tablename(op))
else:
for op in program.global_block().ops:
if is_sparse_op(op):
tablenames.add(get_sparse_tablename(op))
return list(tablenames)
def get_trainers(role_maker):
try:
return role_maker._worker_num()
except Exception:
return role_maker.worker_num()
def get_dense_send_context(
program,
send_ctx,
idx,
merged_dense_pairs,
trainer_id,
split_dense_table=False,
):
if len(merged_dense_pairs) < 1:
return idx
if not split_dense_table:
dense_pairs = []
data_norm_pairs = []
for merged in merged_dense_pairs:
is_data_norm = False
grad = merged[1]
varname = grad.merged_var.name
for name in DATA_NORM_GRAD_NAME:
if varname.endswith(name):
is_data_norm = True
if is_data_norm:
data_norm_pairs.append(merged)
else:
dense_pairs.append(merged)
# simple dense table
origin_varnames = []
var_numel = 0
for merged in dense_pairs:
grad = merged[1]
origin_varnames.append(grad.merged_var.name)
var = program.global_block().vars[grad.merged_var.name]
var_numel += reduce(lambda x, y: x * y, var.shape, 1)
grad_name = "Dense@GRAD_" + str(idx)
aggregate = True
# print("public get_dense_send_context dense_table:", grad_name,
# var_numel, origin_varnames)
from paddle.base.core import CommContext
dense_ctx = CommContext(
grad_name,
[grad_name],
["127.0.0.1:6071"],
[var_numel],
origin_varnames,
trainer_id,
aggregate,
False,
False,
idx,
False,
False,
id(program),
[],
)
send_ctx[grad_name] = dense_ctx
idx += 1
if len(data_norm_pairs) <= 0:
return idx
# data norm table
origin_varnames = []
var_numel = 0
for merged in data_norm_pairs:
grad = merged[1]
origin_varnames.append(grad.merged_var.name)
var = program.global_block().vars[grad.merged_var.name]
var_numel += reduce(lambda x, y: x * y, var.shape, 1)
grad_name = "DataNorm@GRAD_" + str(idx)
aggregate = True
# print("public get_dense_send_context data_norm table:", grad_name,
# var_numel, origin_varnames)
from paddle.base.core import CommContext
data_norm_ctx = CommContext(
grad_name,
[grad_name],
["127.0.0.1:6071"],
[var_numel],
origin_varnames,
trainer_id,
aggregate,
False,
False,
idx,
False,
True,
id(program),
[],
)
send_ctx[grad_name] = data_norm_ctx
idx += 1
else:
for merged in merged_dense_pairs:
grad = merged[1]
origin_varname = grad.merged_var.name
var = program.global_block().vars[origin_varname]
var_numel = reduce(lambda x, y: x * y, var.shape, 1)
grad_name = origin_varname
aggregate = True
from paddle.base.core import CommContext
dense_ctx = CommContext(
grad_name,
[grad_name],
["127.0.0.1:6071"],
[var_numel],
[origin_varname],
trainer_id,
aggregate,
False,
False,
idx,
False,
False,
id(program),
[],
)
send_ctx[grad_name] = dense_ctx
idx += 1
return idx
def get_geo_trainer_send_context(attrs):
if attrs['ps_mode'] != DistributedMode.GEO:
raise ValueError(
f"ps mode: {attrs['ps_mode']} not matched get_geo_trainer_send_context",
)
send_ctx = {}
trainer_id = get_role_id(attrs['role_maker'])
origin_programs = attrs['origin_main_programs']
idx = 0 # table idx
distributed_varnames = get_sparse_tablenames(origin_programs, True)
for i, program in enumerate(origin_programs):
merged_sparse_pairs = attrs['merged_sparse_pairs'][i]
for merged in merged_sparse_pairs:
param, grad = merged
grad_name = grad.merged_var.name
param_name = param.merged_var.name
if param_name in attrs['remote_sparse']: # for recall/ncf model
continue
is_distributed = (
True if param_name in distributed_varnames else False
)
var = program.global_block().vars[grad.merged_var.name]
var_numel = reduce(lambda x, y: x * y, var.shape[1:], 1)
from paddle.base.core import CommContext
print(
"public get_the_geo_send_context sparse: ", grad_name, var_numel
)
sparse_ctx = CommContext(
grad_name,
[grad_name],
["127.0.0.1:6071"],
[var_numel],
[grad_name],
trainer_id,
True,
True,
is_distributed,
idx,
False,
False,
id(program),
[],
)
idx += 1
send_ctx[sparse_ctx.var_name()] = sparse_ctx
if len(send_ctx) == 0:
raise ValueError("GeoSGD require sparse parameters in your net.")
if len(attrs['tensor_table']) > 0 and attrs['is_worker']:
name, ctx = _step_ctx(idx, attrs['role_maker'])
send_ctx[name] = ctx
return send_ctx
def _step_ctx(idx, role_maker):
name = STEP_COUNTER
trainer_id = get_role_id(role_maker)
endpoints = get_ps_endpoints(role_maker)
sections = [1] * len(endpoints)
names = [name] * len(endpoints)
from paddle.base.core import CommContext
ctx = CommContext(
name,
names,
endpoints,
sections,
[name],
trainer_id,
True,
False,
False,
idx,
True,
False,
-1,
[],
)
return name, ctx
def get_the_one_send_context(attrs, split_dense_table=False, ep_list=None):
if ep_list is None:
ep_list = ["127.0.0.1:6071"]
send_ctx = {}
trainer_id = get_role_id(attrs['role_maker'])
origin_programs = attrs['origin_main_programs']
print(f"is_heter_ps_mode? {split_dense_table}")
idx = 0
distributed_varnames = get_sparse_tablenames(origin_programs, True)
# print("public distributed_varnames:", distributed_varnames)
for i, program in enumerate(origin_programs):
merged_sparse_pairs = attrs['merged_sparse_pairs'][i]
for merged in merged_sparse_pairs:
param, grad = merged
grad_name = grad.merged_var.name
param_name = param.merged_var.name
remote_sparse_ids = []
if param_name in attrs['remote_sparse']: # for recall/ncf model
remote_sparse_ids.append(idx)
splited_varname = []
for i in range(len(ep_list)):
splited_varname.append(f"{param_name}.block{i}")
is_distributed = (
True if param_name in distributed_varnames else False
)
var = program.global_block().vars[grad.merged_var.name]
shape = list(var.shape)
shape[0] = 0 if is_distributed else shape[0]
if grad_name in send_ctx:
continue
from paddle.base.core import CommContext
print(
"public get_the_one_send_context sparse: ",
grad_name,
splited_varname,
shape,
)
sparse_ctx = CommContext(
grad_name,
splited_varname,
ep_list,
shape,
[grad_name],
trainer_id,
True,
True,
is_distributed,
idx,
False,
False,
id(program),
remote_sparse_ids,
)
idx += 1
send_ctx[sparse_ctx.var_name()] = sparse_ctx
for i, program in enumerate(origin_programs):
merged_dense_pairs = attrs['merged_dense_pairs'][i]
idx = get_dense_send_context(
program,
send_ctx,
idx,
merged_dense_pairs,
trainer_id,
split_dense_table,
)
if len(attrs['tensor_table']) > 0 and attrs['is_worker']:
name, ctx = _step_ctx(idx, attrs['role_maker'])
send_ctx[name] = ctx
return send_ctx
def find_heter_ops(program, default_device="cpu"):
if default_device not in DEVICE_LIST:
raise ValueError(
f"Given device {default_device} is not in device list {DEVICE_LIST}"
)
def _is_heter_op(op, current_heter_device, default_device="cpu"):
heter_devices = list(DEVICE_LIST)
heter_devices.remove(default_device)
op_device = op.attr("op_device")
op_type = op.type
if op_device in heter_devices:
return True
elif (
op_type in COMMUNICATE_OPS_TYPE
and current_heter_device != default_device
):
# for distributed communicate ops: send & recv & barrier etc.
# Todo: need update this method
# op._set_attr('op_device', current_heter_device)
return True
elif op_device is None or op_device == default_device:
op._set_attr('op_device', default_device)
return False
return False
def _is_same_device(op, pre_device, default_device="cpu"):
op_device = op.attr("op_device")
if op_device == pre_device:
return True
if pre_device == default_device:
return True
return False
def _append_heter_op(op, current_heter_block_ops, heter_ops):
op_device = op.attr("op_device")
if op_device not in heter_ops:
heter_ops[op_device] = {}
current_heter_block_ops.append(op)
origin_program = program.clone()
block = program.global_block()
'''
re-place sum op to fix bug for union forward backward op
'''
var2idx = {}
op_list = list(block.ops)
op_size = len(op_list)
for i in range(op_size - 1, -1, -1):
op_list = list(block.ops)
op = op_list[i]
if "_grad" in op.type:
forward_op_type = op.type.split("_grad")[0]
if (
forward_op_type in SPARSE_OP_TYPE_DICT.keys()
and op.attr('remote_prefetch') is True
):
param_name = op.input(SPARSE_OP_TYPE_DICT[forward_op_type])[0]
if param_name in var2idx:
# insert sum op & remove sum op from var2idx and origin place
op_list = list(block.ops)
sum_op = op_list[var2idx[param_name]]
sum_op_inputs = {
sum_op.input_names[0]: [
block.vars[input]
for input in sum_op.input_arg_names
]
}
sum_op_outputs = {
sum_op.output_names[0]: [
block.vars[output]
for output in sum_op.output_arg_names
]
}
block._insert_op(
index=i + 1,
type=sum_op.type,
inputs=sum_op_inputs,
outputs=sum_op_outputs,
attrs=sum_op.all_attrs(),
)
block._remove_op(var2idx[param_name] + 1)
var2idx.pop(param_name)
for var_ in var2idx:
var2idx[var_] += 1
elif forward_op_type == "elementwise_mul":
"""
get output varname of pre op
"""
output_vars_no_grad = []
for key in op.output_names:
for varname in op.output(key):
if varname == "@EMPTY@":
continue
if "lod_tensor_blocking_queue" in varname:
continue
output_vars_no_grad.append(varname.split("@GRAD")[0])
for no_grad_var in output_vars_no_grad:
if no_grad_var in var2idx:
"""
insert sum op & remove sum op from var2idx and origin place
"""
op_list = list(block.ops)
sum_op = op_list[var2idx[no_grad_var]]
sum_op_inputs = {
sum_op.input_names[0]: [
block.vars[input]
for input in sum_op.input_arg_names
]
}
sum_op_outputs = {
sum_op.output_names[0]: [
block.vars[output]
for output in sum_op.output_arg_names
]
}
block._insert_op(
index=i + 1,
type=sum_op.type,
inputs=sum_op_inputs,
outputs=sum_op_outputs,
attrs=sum_op.all_attrs(),
)
block._remove_op(var2idx[no_grad_var] + 1)
var2idx.pop(no_grad_var)
for var_ in var2idx:
var2idx[var_] += 1
else:
if op.type == "sum":
var = op.output("Out")[0]
if "@GRAD" in var:
origin_var = var.split("@GRAD")[0]
pre_op = op_list[i - 1]
if "_grad" in pre_op.type:
forward_op_type = pre_op.type.split("_grad")[0]
if (
forward_op_type in SPARSE_OP_TYPE_DICT.keys()
and pre_op.attr('remote_prefetch') is True
):
param_name = pre_op.input(
SPARSE_OP_TYPE_DICT[forward_op_type]
)[0]
if param_name == origin_var and op.attr(
"op_device"
) == pre_op.attr("op_device"):
continue
else:
var2idx[origin_var] = i
elif forward_op_type == "elementwise_mul":
output_vars = []
for key in pre_op.output_names:
for varname in pre_op.output(key):
if varname == "@EMPTY@":
continue
if "lod_tensor_blocking_queue" in varname:
continue
output_vars.append(varname)
input_vars = []
for key in op.input_names:
for varname in op.input(key):
if varname == "@EMPTY@":
continue
if "lod_tensor_blocking_queue" in varname:
continue
input_vars.append(varname)
is_match = False
for varname in output_vars:
if varname in input_vars:
is_match = True
break
if is_match:
continue
else:
var2idx[origin_var] = i
else:
var2idx[origin_var] = i
origin_program = program.clone()
block = program.global_block()
program_block_ops = []
default_ops = {default_device: {}}
heter_ops = {}
block_index = 0
current_heter_block_ops = []
current_default_block_ops = []
current_heter_device = default_device
is_heter = False
for op in block.ops:
if _is_heter_op(op, current_heter_device, default_device):
# for gpu/xpu-op
is_heter = True
# for cpu-op block append
if len(current_default_block_ops) > 1:
default_ops[default_device][block_index] = (
current_default_block_ops
)
program_block_ops.append(current_default_block_ops)
current_default_block_ops = []
block_index += 1
if _is_same_device(op, current_heter_device, default_device):
# for gpu-op, gpu-op -> gpu-op,...
current_heter_device = op.attr("op_device")
_append_heter_op(op, current_heter_block_ops, heter_ops)
else:
# for gpu-op -> xpu-op, ...
op_device = current_heter_block_ops[0].attr("op_device")
heter_ops[op_device][block_index] = current_heter_block_ops
program_block_ops.append(current_heter_block_ops)
block_index += 1
current_heter_block_ops = []
current_heter_device = op.attr("op_device")
_append_heter_op(op, current_heter_block_ops, heter_ops)
elif is_heter:
# for gpu/xpu-op -> cpu-op
op_device = current_heter_block_ops[0].attr("op_device")
heter_ops[op_device][block_index] = current_heter_block_ops
program_block_ops.append(current_heter_block_ops)
block_index += 1
current_heter_block_ops = []
current_heter_device = default_device
is_heter = False
current_default_block_ops.append(op)
else:
# for cpu-op
current_default_block_ops.append(op)
if current_default_block_ops != []:
default_ops[default_device][block_index] = current_default_block_ops
program_block_ops.append(current_default_block_ops)
if current_heter_block_ops != []:
op_device = current_heter_block_ops[0].attr("op_device")
heter_ops[op_device][block_index] = current_heter_block_ops
program_block_ops.append(current_heter_block_ops)
if len(heter_ops) == 0:
warnings.warn(
"No heterogeneous OP was found in your program , "
" please using static.device_guard() to run OPs on different device."
)
total_heter_ops = 0
heter_blocks = 0
for device in heter_ops.keys():
heter_block_dict = heter_ops[device]
heter_blocks += len(heter_block_dict)
for _, heter_block in heter_block_dict.items():
total_heter_ops += len(heter_block)
print(
f"There are {len(block.ops)} OPs in your main_program, and contains {total_heter_ops} heter-OPs which is made up of {heter_blocks} heter-blocks."
)
return origin_program, heter_ops, default_ops, program_block_ops
def union_forward_gradient_op(program_block_ops_list):
"""
before analyzing the input & output of each block in program_block_list, we should
union the forward op and corresponding gradient op to eliminate the unnecessary variable
transmit
"""
"""
fix for 2emb model, re-place sum op
"""
block_length = len(program_block_ops_list)
union_program_block_ops_list = []
assert block_length % 2 != 0, (
"the length of program_block_ops_list should be odd"
)
for i in range(0, block_length // 2):
block_op_list = {"forward": program_block_ops_list[i]}
block_op_list.update(
{"backward": program_block_ops_list[block_length - 1 - i]}
)
union_program_block_ops_list.append(block_op_list)
block_op_list = {"forward": [], "backward": []}
for op in program_block_ops_list[block_length // 2]:
if "_grad" not in op.type and not (op.type == "sum"):
block_op_list["forward"].append(op)
else:
block_op_list["backward"].append(op)
union_program_block_ops_list.append(block_op_list)
return union_program_block_ops_list
def find_block_joints(program, program_block_ops_list, heter_ops):
block_var_detail = find_entrance_exit_private(
program, program_block_ops_list
)
block_var_detail = entrance_exit_check(
program, program_block_ops_list, block_var_detail, heter_ops
)
block_var_detail = delete_block_useless_exit(
program, program_block_ops_list, block_var_detail
)
return block_var_detail
def find_ops_list_input_output(program, ops_list):
input_var_list = []
output_var_list = []
for op in ops_list:
inputs = _get_input_map_from_op(program.global_block().vars, op)
input_var_list += get_varlist_from_op_map(inputs)
outputs = _get_output_map_from_op(program.global_block().vars, op)
output_var_list += get_varlist_from_op_map(outputs)
input_var_list = list(set(input_var_list))
output_var_list = list(set(output_var_list))
return input_var_list, output_var_list
def find_entrance_exit_private(program, program_block_ops_list):
block_var_detail = []
persistables = []
for index, block_op_list in enumerate(program_block_ops_list):
# forward
block_input, block_output = find_ops_list_input_output(
program, block_op_list["forward"]
)
persistables = screen_persistables(
program, block_input
) + screen_persistables(program, block_output)
# find entrance & exit
block_private_vars = list(set(block_input) & set(block_output))
block_entrance = list(set(block_input) - set(block_private_vars))
block_exit = list(set(block_output) - set(block_private_vars))
detail = {
"forward": {
"entrance": block_entrance,
"exit": block_exit,
"private": block_private_vars,
"persistables": persistables,
}
}
# backward
bp_block_input, bp_block_output = find_ops_list_input_output(
program, block_op_list["backward"]
)
bp_persistables = screen_persistables(
program, bp_block_input
) + screen_persistables(program, bp_block_output)
# find entrance & exit
bp_block_private_vars = list(set(bp_block_input) & set(bp_block_output))
bp_block_entrance = list(
set(bp_block_input) - set(bp_block_private_vars)
)
bp_block_exit = list(set(bp_block_output) - set(bp_block_private_vars))
detail.update(
{
"backward": {
"entrance": bp_block_entrance,
"exit": bp_block_exit,
"private": bp_block_private_vars,
"persistables": bp_persistables,
}
}
)
block_var_detail.append(detail)
return block_var_detail
def entrance_exit_check(
program, program_block_ops_list, block_var_detail, heter_ops
):
for index in range(len(block_var_detail) - 1, -1, -1):
if index - 1 < 0:
break
previous_block_exit = block_var_detail[index - 1]["forward"]["exit"]
previous_block_exit.sort()
current_block_entrance = block_var_detail[index]["forward"]["entrance"]
backward_entrance = block_var_detail[index]["backward"]["entrance"]
forward_all = (
block_var_detail[index]["forward"]["entrance"]
+ block_var_detail[index]["forward"]["private"]
+ block_var_detail[index]["forward"]["exit"]
)
for var in backward_entrance:
if "@GRAD" not in var and var not in forward_all:
current_block_entrance.append(var)
current_block_entrance.sort()
if previous_block_exit == current_block_entrance:
continue
exist_vars = list(
set(previous_block_exit) & set(current_block_entrance)
)
need_add_vars = list(set(current_block_entrance) - set(exist_vars))
# var in different stage should not be ignored, since they are not placed in the same program & device
# need_add_vars = find_need_var_from_previous_block(
# need_add_vars, block_var_detail, index, heter_ops)
previous_block_private = block_var_detail[index - 1]["forward"][
"private"
]
previous_block_entrance = block_var_detail[index - 1]["forward"][
"entrance"
]
for var in need_add_vars:
if (
var not in previous_block_private
and var not in previous_block_entrance
):
previous_block_entrance.append(var)
previous_block_exit.append(var)
if var not in current_block_entrance:
current_block_entrance.append(var)
for index in range(0, len(block_var_detail) - 1, 1):
previous_block_exit = block_var_detail[index + 1]["backward"]["exit"]
previous_block_exit.sort()
current_block_entrance = block_var_detail[index]["backward"]["entrance"]
current_block_entrance.sort()
if previous_block_exit == current_block_entrance:
continue
exist_vars = list(
set(previous_block_exit) & set(current_block_entrance)
)
need_add_vars = list(set(current_block_entrance) - set(exist_vars))
need_ignore_vars = []
for var in need_add_vars:
if "@GRAD" not in var:
need_ignore_vars.append(var)
need_add_vars = list(
set(need_add_vars).difference(set(need_ignore_vars))
)
previous_block_private = block_var_detail[index + 1]["backward"][
"private"
]
previous_block_entrance = block_var_detail[index + 1]["backward"][
"entrance"
]
for var in need_add_vars:
if (
var not in previous_block_private
and var not in previous_block_entrance
):
previous_block_entrance.append(var)
previous_block_exit.append(var)
return block_var_detail
def delete_block_useless_exit(
program, program_block_ops_list, block_var_detail
):
# forward
for index in range(len(block_var_detail)):
if index == len(block_var_detail) - 1:
break
current_block_exit = block_var_detail[index]["forward"]["exit"]
next_block_entrance = block_var_detail[index + 1]["forward"]["entrance"]
need_delete_var = []
for var in current_block_exit:
if var not in next_block_entrance:
need_delete_var.append(var)
for var in need_delete_var:
current_block_exit.remove(var)
# backward
for index in range(len(block_var_detail) - 1, -1, -1):
if index - 1 < 0:
break
current_block_exit = block_var_detail[index]["backward"]["exit"]
next_block_entrance = block_var_detail[index - 1]["backward"][
"entrance"
]
need_delete_var = []
for var in current_block_exit:
if var not in next_block_entrance:
need_delete_var.append(var)
for var in need_delete_var:
current_block_exit.remove(var)
return block_var_detail
def get_communicate_var_info(
program, block_index, entrance_var_list, type="forward"
):
input_var_reshape_dim = []
input_var_reshape_name = []
if type == "forward":
block_input_var_name = (
f"forward_joint_{block_index - 1}_{block_index}@Heter"
)
else:
block_input_var_name = (
f"backward_joint_{block_index + 1}_{block_index}@Heter"
)
entrance_var_list.sort()
# input
# Heter_SERVER_BLOCK_index@JOINT_VAR -> slice -> var@Heter_SERVER_BLOCK@INPUT_RESHAPE_VAR -> reshape -> var
for name in entrance_var_list:
var = program.global_block().vars[name]
shape = var.shape
recv_var_dim = -1 * reduce(lambda x, y: x * y, shape, 1)
input_var_reshape_dim.append(recv_var_dim)
input_var_reshape_name.append(f"{name}.input_reshape@Heter")
info = {
"input_var_reshape_dim": input_var_reshape_dim,
"input_var_reshape_name": input_var_reshape_name,
"block_input_var_name": block_input_var_name,
}
return info
def add_vars_by_var_list(var_name_list, origin_program, program, block):
for var_name in var_name_list:
if (
var_name not in program.global_block().vars
and var_name not in block.vars
):
var = origin_program.global_block().vars[var_name]
if var.persistable:
program.global_block()._clone_variable(
var, force_persistable=False
)
else:
block._clone_variable(var, force_persistable=False)
def _get_output_map_from_op(varmap, op):
"""Returns a dict from op output name to the vars in varmap."""
iomap = collections.OrderedDict()
for key in op.output_names:
vars = []
for varname in op.output(key):
if varname == "@EMPTY@":
continue
if "lod_tensor_blocking_queue" in varname:
continue
vars.append(varmap[varname])
if len(vars) == 1:
iomap[key] = vars[0]
else:
iomap[key] = vars
return iomap
def get_varlist_from_op_map(var_map):
var_list = []
for key, varlist in var_map.items():
if not isinstance(varlist, list):
varlist = [varlist]
for i in range(len(varlist)):
var = varlist[i]
var_list.append(var.name)
return var_list
def _get_input_map_from_op(varmap, op):
"""Returns a dict from op input name to the vars in varmap."""
iomap = collections.OrderedDict()
for key in op.input_names:
vars = []
for varname in op.input(key):
if varname == "@EMPTY@":
continue
if "lod_tensor_blocking_queue" in varname:
continue
vars.append(varmap[varname])
if len(vars) == 1:
iomap[key] = vars[0]
else:
iomap[key] = vars
return iomap
def screen_persistables(program, var_list):
need_remove = []
for var_name in var_list:
if "@GRAD" in var_name:
if "GRAD" != var_name.split("@")[-1]:
continue
origin_var_name = var_name.split("@GRAD")[0]
var = program.global_block().vars[origin_var_name]
else:
var = program.global_block().vars[var_name]
if is_persistable(var):
need_remove.append(var_name)
for var_name in need_remove:
var_list.remove(var_name)
return need_remove
def block_append_op(program, origin_program, block, op):
merge_ordereddict = origin_program.global_block().vars.copy()
merge_ordereddict.update(block.vars)
inputs = _get_input_map_from_op(merge_ordereddict, op)
for key, varlist in inputs.items():
if not isinstance(varlist, list):
varlist = [varlist]
for var in varlist:
if (
var.name not in program.global_block().vars
and var.name not in block.vars
):
if var.persistable:
program.global_block()._clone_variable(
var, force_persistable=False
)
else:
block._clone_variable(var, force_persistable=False)
outputs = _get_output_map_from_op(origin_program.global_block().vars, op)
for key, varlist in outputs.items():
if not isinstance(varlist, list):
varlist = [varlist]
for var in varlist:
if (
var.name not in program.global_block().vars
and var.name not in block.vars
):
if var.persistable:
program.global_block()._clone_variable(
var, force_persistable=False
)
else:
block._clone_variable(var, force_persistable=False)
if "_grad" not in op.type:
# for forward op
return block.append_op(
type=op.type, inputs=inputs, outputs=outputs, attrs=op.all_attrs()
)
else:
# for grad op
op_desc = op.desc
backward = core.op_proto_and_checker_maker.OpRole.Backward
device_attr_name = core.op_proto_and_checker_maker.kOpDeviceAttrName()
# append grad op
new_op_desc = block.desc.append_op()
new_op_desc.copy_from(op_desc)
new_op_desc._set_attr(RPC_OP_ROLE_ATTR_NAME, backward)
# set device grad
if op.desc.has_attr(device_attr_name):
op_device = op_desc.attr(device_attr_name)
new_op_desc._set_attr(device_attr_name, op_device)
block._sync_with_cpp()
def get_next_stage_trainers(role_maker):
try:
return role_maker._get_next_trainers()
except Exception:
return role_maker.get_next_trainers()
def insert_communicate_op(
origin_program,
role_maker,
heter_block,
stage_id,
first_op_index,
block_var_detail,
device,
is_forward=True,
):
if is_forward:
next_heter_worker_endpoints = get_next_stage_trainers(role_maker)
previous_heter_worker_endpoints = get_previous_stage_trainers(
role_maker
)
entrance_var = block_var_detail[stage_id]["forward"]["entrance"]
comm_info = get_communicate_var_info(
origin_program, stage_id + 1, entrance_var
)
else:
next_heter_worker_endpoints = get_next_stage_trainers(role_maker)
previous_heter_worker_endpoints = get_previous_stage_trainers(
role_maker
)
entrance_var = block_var_detail[stage_id - 1]["backward"]["exit"]
comm_info = get_communicate_var_info(
origin_program, stage_id - 1, entrance_var, "backward"
)
heter_block._insert_op(
index=first_op_index,
type="send_and_recv",
inputs={"X": heter_block.vars[entrance_var[0]]},
outputs={"Out": []},
attrs={
"mode": "forward" if is_forward else "backward",
"send_var_name": [*entrance_var, "microbatch_id"],
"recv_var_name": [],
"message_name": comm_info["block_input_var_name"],
"next_endpoints": next_heter_worker_endpoints,
"previous_endpoints": previous_heter_worker_endpoints,
"trainer_id": get_role_id(role_maker),
"op_device": device,
RPC_OP_ROLE_ATTR_NAME: RPC_OP_ROLE_ATTR_VALUE,
},
)
return entrance_var
def get_the_one_recv_context(context, is_dense=True, split_dense_table=False):
recv_id_maps = {}
grad_name_to_param_name = {}
if is_dense:
send_ctx = get_the_one_send_context(
context, split_dense_table=split_dense_table
)
for idx, (name, ctx) in enumerate(send_ctx.items()):
if ctx.is_sparse():
continue
if ctx.is_tensor_table():
continue
origin_grad_varnames = ctx.origin_varnames()
param_names = []
for grad_varname in origin_grad_varnames:
param_name = context["grad_name_to_param_name"][grad_varname]
param_names.append(param_name)
recv_id_maps[ctx.table_id()] = param_names
else:
send_ctx = get_the_one_send_context(
context, split_dense_table=False, ep_list=None
)
for idx, (name, ctx) in enumerate(send_ctx.items()):
if not ctx.is_sparse():
continue
origin_grad_varnames = ctx.origin_varnames()
param_names = []
for grad_varname in origin_grad_varnames:
param_name = context["grad_name_to_param_name"][grad_varname]
param_names.append(param_name)
recv_id_maps[ctx.table_id()] = param_names
return recv_id_maps
def _get_varname_parts(varname):
# returns origin, blockid, trainerid
orig_var_name = ""
trainer_part = ""
block_part = ""
trainer_idx = varname.find(".trainer_")
if trainer_idx >= 0:
trainer_part = varname[trainer_idx + 1 :]
else:
trainer_idx = len(varname)
block_index = varname.find(".block")
if block_index >= 0:
block_part = varname[block_index + 1 : trainer_idx]
else:
block_index = len(varname)
orig_var_name = varname[0 : min(block_index, trainer_idx)]
return orig_var_name, block_part, trainer_part
dtype_to_size = {
core.VarDesc.VarType.FP16: 2,
core.VarDesc.VarType.FP32: 4,
core.VarDesc.VarType.FP64: 8,
core.VarDesc.VarType.INT16: 2,
core.VarDesc.VarType.INT32: 4,
core.VarDesc.VarType.INT64: 8,
core.VarDesc.VarType.BOOL: 1,
core.VarDesc.VarType.UINT8: 1,
}
def get_var_mem_size(var):
m_size = reduce(lambda x, y: x * y, var.shape, 1)
m_size *= dtype_to_size[var.dtype]
return m_size
class MergedVariable:
def __init__(self, merged, ordered, offsets):
self.merged_var = merged
self.ordered_vars = ordered
self.offsets = offsets
def build_var_distributed(context):
origin_programs = context['origin_main_programs']
param_name_to_grad_name = {}
grad_name_to_param_name = {}
context["origin_sparse_pairs"] = []
context["origin_dense_pairs"] = []
context["merged_sparse_pairs"] = []
context['merged_dense_pairs'] = []
context["merged_variables_pairs"] = []
context["merged_variable_map"] = {}
for origin_program in origin_programs:
sparse_pairs, dense_pairs = get_param_grads(origin_program)
# print("public build_var_distributed sparse_pairs:", sparse_pairs)
# print("public build_var_distributed dense_pairs:", dense_pairs)
origin_for_sparse = []
origin_for_dense = []
merged_sparse_pairs = []
merged_dense_pairs = []
merged_variables_pairs = []
for param, grad in sparse_pairs:
origin_for_sparse.append((param, grad))
for param, grad in dense_pairs:
origin_for_dense.append((param, grad))
for dense_pair in origin_for_dense:
param, grad = dense_pair
m_param = MergedVariable(param, [param], [0])
m_grad = MergedVariable(grad, [grad], [0])
merged_variables_pairs.append((m_param, m_grad))
merged_dense_pairs.append((m_param, m_grad))
# print("public build_var_distributed merged_dense_pairs:",
# merged_dense_pairs)
for sparse_pair in origin_for_sparse:
param, grad = sparse_pair
m_param = MergedVariable(param, [param], [0])
m_grad = MergedVariable(grad, [grad], [0])
merged_variables_pairs.append((m_param, m_grad))
merged_sparse_pairs.append((m_param, m_grad))
# print("public build_var_distributed merged_sparse_pairs:",
# merged_sparse_pairs)
for merged in merged_variables_pairs:
m_param, m_grad = merged
context["merged_variable_map"][m_param.merged_var.name] = (
m_param.merged_var
)
context["merged_variable_map"][m_grad.merged_var.name] = (
m_grad.merged_var
)
param_merges = []
param_merges.extend(origin_for_sparse)
param_merges.extend(origin_for_dense)
for param, grad in param_merges:
param_name_to_grad_name[param.name] = grad.name
grad_name_to_param_name[grad.name] = param.name
context["origin_sparse_pairs"].append(origin_for_sparse)
context["origin_dense_pairs"].append(origin_for_dense)
context["merged_sparse_pairs"].append(merged_sparse_pairs)
context['merged_dense_pairs'].append(merged_dense_pairs)
context["param_name_to_grad_name"] = param_name_to_grad_name
context["grad_name_to_param_name"] = grad_name_to_param_name
'''
print("public build_var_distributed origin_sparse_pairs:",
context["origin_sparse_pairs"])
print("public build_var_distributed origin_for_dense:",
context["origin_dense_pairs"])
print("public build_var_distributed merged_sparse_pairs:",
context["merged_sparse_pairs"])
print("public build_var_distributed merged_dense_pairs:",
context['merged_dense_pairs'])
print("public build_var_distributed param_name_to_grad_name:",
param_name_to_grad_name)
print("public build_var_distributed grad_name_to_param_name:",
grad_name_to_param_name)
'''
def _is_opt_role_op(op):
# NOTE : depend on oprole to find out whether this op is for
# optimize
op_maker = core.op_proto_and_checker_maker
optimize_role = core.op_proto_and_checker_maker.OpRole.Optimize
if op_maker.kOpRoleAttrName() in op.attr_names and int(
op.all_attrs()[op_maker.kOpRoleAttrName()]
) == int(optimize_role):
return True
return False
def get_param_grads(origin_program):
def _get_params_grads(sparse_varnames):
block = origin_program.global_block()
dense_param_grads = []
sparse_param_grads = []
optimize_params = set()
origin_var_dict = origin_program.global_block().vars
role_id = int(core.op_proto_and_checker_maker.OpRole.Backward)
for op in block.ops:
if _is_opt_role_op(op):
# delete clip op from opt_ops when run in Parameter Server mode
if (
OP_NAME_SCOPE in op.all_attrs()
and CLIP_OP_NAME_SCOPE in op.attr(OP_NAME_SCOPE)
):
op._set_attr("op_role", role_id)
continue
if not op.has_attr(OP_ROLE_VAR_ATTR_NAME):
continue
if op.attr(OP_ROLE_VAR_ATTR_NAME):
param_name = op.attr(OP_ROLE_VAR_ATTR_NAME)[0]
grad_name = op.attr(OP_ROLE_VAR_ATTR_NAME)[1]
if param_name not in optimize_params:
optimize_params.add(param_name)
param_grad = (
origin_var_dict[param_name],
origin_var_dict[grad_name],
)
if param_name in sparse_varnames:
sparse_param_grads.append(param_grad)
else:
dense_param_grads.append(param_grad)
return sparse_param_grads, dense_param_grads
def _get_sparse_varnames():
varnames = []
for op in origin_program.global_block().ops:
if (
op.type in SPARSE_OP_TYPE_DICT.keys()
and op.attr('remote_prefetch') is True
):
param_name = op.input(SPARSE_OP_TYPE_DICT[op.type])[0]
varnames.append(param_name)
return list(set(varnames))
sparse_varnames = _get_sparse_varnames()
sparse_param_grads, dense_param_grads = _get_params_grads(sparse_varnames)
return sparse_param_grads, dense_param_grads
def delete_ops(block, ops):
for op in ops:
try:
idx = list(block.ops).index(op)
block._remove_op(idx)
except Exception as e:
print(e)
def find_send_op(program):
send_op_list = []
for op in program.global_block().ops:
if op.type == "send":
send_op_list.append(op)
return send_op_list
def find_op_input_output(program, block, op):
input_var_list = []
output_var_list = []
inputs = _get_input_map_from_op(block.vars, op)
input_var_list += get_varlist_from_op_map(inputs)
outputs = _get_output_map_from_op(block.vars, op)
output_var_list += get_varlist_from_op_map(outputs)
input_var_list = list(set(input_var_list))
output_var_list = list(set(output_var_list))
return input_var_list, output_var_list
def add_send_op(program, block, _vars):
def _get_send_op_dict():
send_op_dict = {}
send_op_list = find_send_op(program)
for op in send_op_list:
input_list, _ = find_op_input_output(
program, program.global_block(), op
)
for var in input_list:
send_op_dict[var] = op
return send_op_dict
send_grad_var_list = []
send_op_dict = _get_send_op_dict()
table_dict = {}
for persistable_var in _vars:
if "@GRAD" not in persistable_var:
continue
if "GRAD" != persistable_var.split("@")[-1]:
continue
if persistable_var not in send_op_dict:
continue
send_op = send_op_dict[persistable_var]
is_sparse = send_op.attr('is_sparse')
table_id = send_op.attr('table_id')
send_varnames = send_op.attr('send_varnames')
send_grad_var_list.append(persistable_var)
if table_id not in table_dict:
table_dict[table_id] = {}
table_dict[table_id]['var_list'] = []
table_dict[table_id]['is_sparse'] = is_sparse
table_dict[table_id]['send_varnames'] = send_varnames
table_dict[table_id]['var_list'].append(persistable_var)
for table_id in table_dict:
dummy_output = block.create_var(name=generate_control_dev_var_name())
send_input_vars = [
block.vars[union_var]
for union_var in table_dict[table_id]['var_list']
]
block.append_op(
type="send",
inputs={"X": send_input_vars},
outputs={"Out": dummy_output},
attrs={
"send_varnames": table_dict[table_id]['send_varnames'],
"is_sparse": is_sparse,
"table_id": table_id,
RPC_OP_ROLE_ATTR_NAME: RPC_OP_ROLE_ATTR_VALUE,
},
)
return send_grad_var_list
def get_vars_name_in_block(block):
vars_list = block.vars.keys()
vars_name_list = list(vars_list)
return vars_name_list
# reserve static_var
def delete_trainer_useless_var(program, static_var):
static_var = list(set(static_var))
program_useful_var_list = []
for op in program.global_block().ops:
input_var_list, output_var_list = find_op_input_output(
program, program.global_block(), op
)
op_var_list = list(set(input_var_list).union(set(output_var_list)))
program_useful_var_list = list(
set(program_useful_var_list).union(set(op_var_list))
)
program_useful_var_list += static_var
program_useless_var_list = list(
set(get_vars_name_in_block(program.global_block())).difference(
set(program_useful_var_list)
)
)
for var in program_useless_var_list:
program.global_block()._remove_var(var)
return program_useless_var_list
def create_backward_block(
program, origin_program, bp_ops_list, block_var_detail
):
pre_block_idx = program.num_blocks - 1
heter_block = program._create_block(pre_block_idx)
for _, op in enumerate(bp_ops_list):
if op.type == "send":
send_varnames = op.attr('send_varnames')
is_skip = False
for varname in send_varnames:
if (
varname not in program.global_block().vars
and varname not in heter_block.vars
):
is_skip = True
break
if is_skip:
continue
block_append_op(program, origin_program, heter_block, op)
entrance_vars = block_var_detail[0]["backward"]["entrance"]
add_vars_by_var_list(entrance_vars, origin_program, program, heter_block)
exit_vars = block_var_detail[0]["backward"]["exit"]
add_vars_by_var_list(exit_vars, origin_program, program, heter_block)
return heter_block
def is_backward_op(op):
return op_role_attr_name in op.attr_names and (
int(op.attr(op_role_attr_name)) & int(op_role.Backward)
)
def is_forward_op(op):
return op_role_attr_name in op.attr_names and (
int(op.attr(op_role_attr_name)) == int(op_role.Forward)
)
def is_push_sparse_op(op):
return op.type == 'distributed_push_sparse'
def get_distributed_push_sparse_op_list(block):
push_sparse_op_list = []
for op_idx in range(block.desc.op_size()):
op = block.ops[op_idx]
if is_push_sparse_op(op):
push_sparse_op_list.append(op)
return push_sparse_op_list
def get_bp_op_list(block):
bp_op_list = []
for op_idx in range(block.desc.op_size()):
op = block.ops[op_idx]
if is_backward_op(op):
bp_op_list.append(op)
return bp_op_list
def delete_same_ops(block, ops):
for op in ops:
try:
for origin_op in block.ops:
if str(origin_op) == str(op):
idx = list(block.ops).index(origin_op)
block._remove_op(idx)
break
except Exception as e:
print(e)
def check_program(program):
block_idx = 0
for block in program.blocks:
for op in block.ops:
input_var_names = op.desc.input_arg_names()
output_var_names = op.desc.output_arg_names()
for var_name in input_var_names + output_var_names:
if not block._find_var_recursive(str(var_name)):
raise ValueError(
f'var: {var_name} needed by op is not found in block: {block_idx}'
)
block_idx += 1
print('program checked valid')
def debug_program(file, program):
# py >= 3.2
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, 'w+') as f:
f.write(str(program))
def is_distributed_env():
node_role = os.getenv("TRAINING_ROLE")
if node_role is None:
return False
else:
return True