Files
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

312 lines
12 KiB
Python

# 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 os
import paddle
import paddle.distributed as dist
from paddle import _C_ops
from paddle.distributed import fleet
from paddle.distributed.fleet.utils.log_util import logger
from paddle.framework import core
_raise_cuda_env_unset_warning = True
_mp_async_allreduce = False
_sp_async_reduce_scatter = False
def _check_environment_for_overlap():
if int(os.getenv("CUDA_DEVICE_MAX_CONNECTIONS", "0")) != 1:
global _raise_cuda_env_unset_warning
if _raise_cuda_env_unset_warning:
logger.warning(
"You set enable_mp_async_allreduce or enable_sp_async_reduce_scatter, but you forget to set environment "
"variable CUDA_DEVICE_MAX_CONNECTIONS=1, which may leads to performance "
"loss. Try to export CUDA_DEVICE_MAX_CONNECTIONS=1 for better performance."
)
_raise_cuda_env_unset_warning = False
def is_fused_matmul_bias_supported():
if paddle.is_compiled_with_cuda() and not paddle.is_compiled_with_rocm() or paddle.is_compiled_with_xpu():
return hasattr(core.eager.ops.legacy, "fused_gemm_epilogue")
else:
return False
if is_fused_matmul_bias_supported():
origin_linear = paddle.incubate.nn.functional.fused_linear
else:
origin_linear = paddle.nn.functional.linear
def mp_async_allreduce(x_grad):
if _mp_async_allreduce and x_grad.process_mesh is not None:
_check_environment_for_overlap()
mp_placement_index = x_grad.process_mesh.dim_names.index("mp")
if mp_placement_index != -1 and x_grad.placements[mp_placement_index].is_partial():
hcg = fleet.get_hybrid_communicate_group()
model_parallel_group = hcg.get_model_parallel_group()
task = dist.stream.all_reduce(
x_grad._local_value(),
group=model_parallel_group,
sync_op=False,
)
return task
else:
return None
else:
return None
def sp_async_reducesctter(x_grad):
if _sp_async_reduce_scatter and x_grad.process_mesh is not None:
_check_environment_for_overlap()
mp_placement_index = x_grad.process_mesh.dim_names.index("mp")
if mp_placement_index != -1 and x_grad.placements[mp_placement_index].is_partial():
hcg = fleet.get_hybrid_communicate_group()
model_parallel_group = hcg.get_model_parallel_group()
parallelism = model_parallel_group.nranks
assert (
x_grad.shape[0] % parallelism == 0
), f"Input sequence length {x_grad.shape[0]} can't be divided exactly by sequence parallelism {parallelism}"
# reduce-scatter dx
x_grad_global_shape = x_grad.shape
x_grad_global_shape[0] = x_grad_global_shape[0] // parallelism
x_grad_local = x_grad._local_value()
x_grad_local_shape = x_grad_local.shape
x_grad_local_shape[0] = x_grad_local_shape[0] // parallelism
dx_local = paddle.empty(shape=x_grad_local_shape, dtype=x_grad.dtype)
task = dist.stream.reduce_scatter(
dx_local,
x_grad_local,
op=dist.ReduceOp.SUM,
group=model_parallel_group,
sync_op=False,
)
return task, dx_local, x_grad_global_shape
else:
return None
else:
return None
def sync_mp_allreduce(task, dist_tensor):
mp_placement_index = dist_tensor.process_mesh.dim_names.index("mp")
new_placments = list()
for idx, placement in enumerate(dist_tensor.placements):
if idx == mp_placement_index:
new_placments.append(dist.Replicate())
else:
new_placments.append(placement)
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
task.wait()
return paddle.Tensor(
dist_tensor._local_value(),
dims=dist_tensor.shape,
process_mesh=dist_tensor.process_mesh,
placements=new_placments,
place=place,
)
def sync_sp_reducescatter(task, dist_tensor):
task, dx_local, x_grad_global_shape = task
placements = [dist.Shard(1), dist.Shard(0)]
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
task.wait()
return paddle.Tensor(
dx_local,
dims=x_grad_global_shape,
process_mesh=dist_tensor.process_mesh,
placements=placements,
place=place,
)
# modify from Paddle/python/paddle/distributed/auto_parallel/moe_utils.py
def _dist_reshape(dist_tensor):
local_tensor = dist_tensor._local_value()
tgt_global_shape = [dist_tensor.shape[0] * dist_tensor.shape[1], dist_tensor.shape[2]]
tgt_local_shape = [local_tensor.shape[0] * local_tensor.shape[1], local_tensor.shape[2]]
place = paddle.framework._current_expected_place()
place = paddle.framework._get_paddle_place(place)
local_tensor = local_tensor.reshape(tgt_local_shape)
if dist_tensor.placements[1].is_shard():
new_placements = [dist.Shard(0), dist.Shard(1)]
else:
new_placements = [dist.Shard(0), dist.Replicate()]
out = paddle.Tensor(
local_tensor,
dims=tgt_global_shape,
process_mesh=dist_tensor.process_mesh,
placements=new_placements,
place=place,
)
out.stop_gradient = dist_tensor.stop_gradient
return out
class FusedLinearWithGradAdd(paddle.autograd.PyLayer):
@staticmethod
def forward(ctx, x, weight, bias=None, name=None):
y = origin_linear(x, weight, bias)
ctx.save_for_backward(x, weight, bias)
return y
@staticmethod
def backward(ctx, y_grad):
x, weight, bias = ctx.saved_tensor()
x_grad = paddle.matmul(y_grad, weight, transpose_y=True)
if _sp_async_reduce_scatter:
task = sp_async_reducesctter(x_grad)
else:
task = mp_async_allreduce(x_grad)
# _C_ops.fused_linear_param_grad_add(x, y_grad, weight_grad, bias_grad, multi precision, has bias)
if bias is None:
if hasattr(weight, "main_grad"):
weight.main_grad, _ = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.main_grad, None, True, False
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None
else:
if weight.grad is not None:
weight.grad, _ = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.grad, None, False if weight.grad.dtype != paddle.float32 else True, False
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None
else:
weight_grad, _ = _C_ops.fused_linear_param_grad_add(x, y_grad, None, None, False, False)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad
if hasattr(weight, "main_grad") and hasattr(bias, "main_grad"):
weight.main_grad, bias.main_grad = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.main_grad, bias.main_grad, True, True
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None, None
else:
if weight.grad is not None:
assert bias.grad is not None
weight.grad, bias.grad = _C_ops.fused_linear_param_grad_add(
x, y_grad, weight.grad, bias.grad, False if weight.grad.dtype != paddle.float32 else True, True
)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, None, None
else:
weight_grad, bias_grad = _C_ops.fused_linear_param_grad_add(x, y_grad, None, None, False, True)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad, bias_grad
class OverlapLinear(paddle.autograd.PyLayer):
@staticmethod
def forward(ctx, x, weight, bias=None, name=None):
y = origin_linear(x, weight, bias)
ctx.save_for_backward(x, weight, bias)
return y
@staticmethod
def backward(ctx, y_grad):
x, weight, bias = ctx.saved_tensor()
x_grad = paddle.matmul(y_grad, weight, transpose_y=True)
if _sp_async_reduce_scatter:
task = sp_async_reducesctter(x_grad)
else:
task = mp_async_allreduce(x_grad)
if _sp_async_reduce_scatter:
y_grad = _dist_reshape(y_grad)
else:
y_grad = y_grad.reshape([-1, y_grad.shape[-1]])
weight_grad = paddle.matmul(
_dist_reshape(x) if _sp_async_reduce_scatter else x.reshape([-1, x.shape[-1]]),
y_grad,
transpose_x=True,
)
if bias is None:
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad
else:
bias_grad = paddle.sum(y_grad, axis=0)
if task is not None:
if _sp_async_reduce_scatter:
x_grad = sync_sp_reducescatter(task, x_grad)
else:
x_grad = sync_mp_allreduce(task, x_grad)
return x_grad, weight_grad, bias_grad
def mock_layers(
enable_fused_linear_grad_add=True, enable_mp_async_allreduce=False, enable_sp_async_reduce_scatter=False
):
global _mp_async_allreduce
global _sp_async_reduce_scatter
_mp_async_allreduce = enable_mp_async_allreduce
_sp_async_reduce_scatter = enable_sp_async_reduce_scatter
if enable_fused_linear_grad_add:
paddle.nn.functional.linear = FusedLinearWithGradAdd.apply
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = FusedLinearWithGradAdd.apply
else:
paddle.nn.functional.linear = OverlapLinear.apply
if is_fused_matmul_bias_supported():
paddle.incubate.nn.functional.fused_linear = OverlapLinear.apply