Files
paddlepaddle--paddle/paddle/phi/infermeta/spmd_rules/tile.cc
T
2026-07-13 12:40:42 +08:00

292 lines
11 KiB
C++

/* 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. */
#include "paddle/phi/infermeta/spmd_rules/tile.h"
#include "glog/logging.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/infermeta/spmd_rules/utils.h"
namespace phi {
namespace distributed {
namespace {
std::vector<int64_t> GetRepeatTimes(const std::vector<int64_t>& repeat_times,
int x_ndim) {
auto repeat_times_new = repeat_times;
if (x_ndim > static_cast<int>(repeat_times.size())) {
size_t diff = static_cast<size_t>(x_ndim) - repeat_times.size();
for (size_t i = 0; i < diff; ++i) {
repeat_times_new.insert(repeat_times_new.begin(), 1);
}
}
return repeat_times_new;
}
} // anonymous namespace
SpmdInfo TileInferSpmd(const DistMetaTensor& x,
const std::vector<int64_t>& repeat_times) {
auto x_shape = vectorize(x.dims());
int x_ndim = x_shape.size();
const auto& x_dist_attr_src = x.dist_attr();
const std::vector<std::vector<int64_t>>& x_dims_mapping =
x_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
x_ndim,
x_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor x's rank [%d] and Input's "
"dims_mapping size [%d] are not matched.",
x_ndim,
x_dims_mapping.size()));
auto repeat_times_new = GetRepeatTimes(repeat_times, x_ndim);
int64_t broadcast_dims = repeat_times_new.size() - x_ndim;
std::vector<int64_t> dims_to_unshard;
for (int64_t i = broadcast_dims;
i < static_cast<int64_t>(repeat_times_new.size());
++i) {
if (repeat_times_new[i] == 1) {
continue;
}
dims_to_unshard.push_back(i - broadcast_dims);
}
auto x_dist_attr_dst = UnShardTensorDims(x_dist_attr_src, dims_to_unshard);
std::vector<std::vector<int64_t>> out_dims_mapping(repeat_times_new.size(),
std::vector<int64_t>({}));
const auto& x_dims_mapping_dst = x_dist_attr_dst.multi_dims_mapping();
for (int64_t i = broadcast_dims;
i < static_cast<int64_t>(repeat_times_new.size());
i++) {
out_dims_mapping[i] = x_dims_mapping_dst[i - broadcast_dims];
}
auto out_dist_attr = CopyTensorDistAttrForOutput(x_dist_attr_dst);
out_dist_attr.set_dims_mapping(out_dims_mapping);
VLOG(4) << "TileInferSpmd:";
VLOG(4) << "x shape: [" << str_join(x_shape) << "]"
<< "src_dims_mapping: ["
<< str_join(x_dist_attr_src.multi_dims_mapping()) << "] "
<< "dst_dims_mapping: ["
<< str_join(x_dist_attr_dst.multi_dims_mapping()) << "]";
VLOG(4) << "Output"
<< " dims_mapping: [" << str_join(out_dist_attr.multi_dims_mapping())
<< "]";
VLOG(4) << std::endl;
return {{x_dist_attr_dst}, {out_dist_attr}};
}
SpmdInfo TileInferSpmdDynamic(const DistMetaTensor& x,
const IntArray& repeat_times) {
return TileInferSpmd(x, repeat_times.GetData());
}
SpmdInfo TileInferSpmdReverse(const DistMetaTensor& x,
const DistMetaTensor& out,
const std::vector<int64_t>& repeat_times) {
auto x_shape = vectorize(x.dims());
int x_ndim = x_shape.size();
const auto& x_dist_attr_src = x.dist_attr();
const std::vector<std::vector<int64_t>>& x_dims_mapping =
x_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
x_ndim,
x_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor x's rank [%d] and Input's "
"dims_mapping size [%d] are not matched.",
x_ndim,
x_dims_mapping.size()));
auto repeat_times_new = GetRepeatTimes(repeat_times, x_ndim);
auto out_shape = vectorize(out.dims());
int out_ndim = out_shape.size();
const auto& out_dist_attr_src = out.dist_attr();
const std::vector<std::vector<int64_t>>& out_dims_mapping =
out_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
out_ndim,
out_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor out's rank [%d] and Input's "
"dims_mapping size [%d] are not matched.",
out_ndim,
out_dims_mapping.size()));
PADDLE_ENFORCE_EQ(out_ndim,
repeat_times_new.size(),
common::errors::InvalidArgument(
"The Tensor out's rank [%d] and repeat_times_new's "
"size [%d] are not matched.",
out_ndim,
repeat_times_new.size()));
int64_t broadcast_dims = repeat_times_new.size() - x_ndim;
std::vector<int64_t> dims_to_unshard;
for (int64_t i = broadcast_dims;
i < static_cast<int64_t>(repeat_times_new.size());
++i) {
if (repeat_times_new[i] == 1) {
continue;
}
dims_to_unshard.push_back(i);
}
auto out_dist_attr_dst =
UnShardTensorDims(out_dist_attr_src, dims_to_unshard);
const auto& out_dims_mapping_dst = out_dist_attr_dst.multi_dims_mapping();
std::vector<std::vector<int64_t>> x_dims_mapping_dst(
x_ndim, std::vector<int64_t>({}));
for (int64_t i = 0; i < static_cast<int64_t>(x_ndim); i++) {
x_dims_mapping_dst[i] = out_dims_mapping_dst[i + broadcast_dims];
}
auto x_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src);
x_dist_attr_dst.set_dims_mapping(x_dims_mapping_dst);
VLOG(4) << "TileInferSpmdReverse:";
VLOG(4) << "out shape: [" << str_join(out_shape) << "]"
<< "src_dims_mapping: ["
<< str_join(out_dist_attr_src.multi_dims_mapping()) << "] "
<< "dst_dims_mapping: ["
<< str_join(out_dist_attr_dst.multi_dims_mapping()) << "]";
VLOG(4) << "x: "
<< "dst_dims_mapping: ["
<< str_join(x_dist_attr_dst.multi_dims_mapping()) << "]";
return {{x_dist_attr_dst}, {out_dist_attr_dst}};
}
SpmdInfo TileGradInferSpmd(const DistMetaTensor& x,
const DistMetaTensor& out_grad,
const std::vector<int64_t>& repeat_times) {
auto x_shape = vectorize(x.dims());
int x_ndim = x_shape.size();
const auto& x_dist_attr_src = x.dist_attr();
const std::vector<std::vector<int64_t>>& x_dims_mapping =
x_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
x_ndim,
x_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor x's rank [%d] and Input's "
"dims_mapping size [%d] are not matched.",
x_ndim,
x_dims_mapping.size()));
auto repeat_times_new = GetRepeatTimes(repeat_times, x_ndim);
auto out_grad_shape = vectorize(out_grad.dims());
int out_grad_ndim = out_grad_shape.size();
const auto& out_grad_dist_attr_src = out_grad.dist_attr();
const std::vector<std::vector<int64_t>>& out_grad_dims_mapping =
out_grad_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(out_grad_ndim,
out_grad_dims_mapping.size(),
common::errors::InvalidArgument(
"The Tensor out_grad's rank [%d] and Input's "
"dims_mapping size [%d] are not matched.",
out_grad_ndim,
out_grad_dims_mapping.size()));
PADDLE_ENFORCE_EQ(
out_grad_ndim,
repeat_times_new.size(),
common::errors::InvalidArgument(
"The Tensor out_grad's rank [%d] and repeat_times_new's "
"size [%d] are not matched.",
out_grad_ndim,
repeat_times_new.size()));
int64_t broadcast_dims = repeat_times_new.size() - x_ndim;
std::vector<int64_t> dims_to_unshard_for_x;
std::vector<int64_t> dims_to_unshard_for_out;
for (int64_t i = broadcast_dims;
i < static_cast<int64_t>(repeat_times_new.size());
++i) {
if (repeat_times_new[i] == 1) {
continue;
}
dims_to_unshard_for_x.push_back(i - broadcast_dims);
dims_to_unshard_for_out.push_back(i);
}
auto x_dist_attr_dst =
UnShardTensorDims(x_dist_attr_src, dims_to_unshard_for_x);
auto out_grad_dist_attr_dst =
UnShardTensorDims(out_grad_dist_attr_src, dims_to_unshard_for_out);
std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
std::string x_axes = alphabet.substr(broadcast_dims, x_ndim);
std::string out_grad_axes = alphabet.substr(0, out_grad_ndim);
std::vector<std::pair<std::string, std::vector<std::vector<int64_t>>>>
axes_sharding_info;
axes_sharding_info.emplace_back(x_axes, x_dist_attr_dst.multi_dims_mapping());
axes_sharding_info.emplace_back(out_grad_axes,
out_grad_dist_attr_dst.multi_dims_mapping());
const auto& axis_size =
GetAxesSizes({{x_axes, x_shape}, {out_grad_axes, out_grad_shape}}, false);
const auto& mesh_shape = out_grad_dist_attr_src.process_mesh().shape();
auto axis_to_dim_map =
ShardingMergeForTensors(axes_sharding_info, axis_size, mesh_shape);
auto x_dim_mapping_dst = GetDimsMappingForAxes(x_axes, axis_to_dim_map, true);
auto out_grad_dim_mapping_dst =
GetDimsMappingForAxes(out_grad_axes, axis_to_dim_map, true);
x_dist_attr_dst.set_dims_mapping(x_dim_mapping_dst);
out_grad_dist_attr_dst.set_dims_mapping(out_grad_dim_mapping_dst);
auto x_grad_dist_attr = CopyTensorDistAttrForOutput(x_dist_attr_dst);
x_grad_dist_attr.set_dims_mapping(x_dim_mapping_dst);
// partial grad dim
std::vector<int64_t> partial_on_dims;
const auto& dim_mapping = out_grad_dist_attr_dst.multi_dims_mapping();
for (int i = 0; i < broadcast_dims; ++i) {
auto mapping = dim_mapping[i];
if (!mapping.empty()) {
for (const auto& dim : mapping) {
partial_on_dims.push_back(dim);
}
}
}
x_grad_dist_attr.set_partial_status(partial_on_dims);
VLOG(4) << "TileGradInferSpmd:";
VLOG(4) << "x: " << str_join(x_shape) << "]"
<< "src_dims_mapping: ["
<< str_join(x_dist_attr_src.multi_dims_mapping()) << "] "
<< "dst_dims_mapping: ["
<< str_join(x_dist_attr_dst.multi_dims_mapping()) << "]";
VLOG(4) << "out_grad: " << str_join(out_grad_shape) << "]"
<< "src_dims_mapping: ["
<< str_join(out_grad_dist_attr_src.multi_dims_mapping()) << "] "
<< "dst_dims_mapping: ["
<< str_join(out_grad_dist_attr_dst.multi_dims_mapping()) << "]";
VLOG(4) << "x grad"
<< "dst_dims_mapping: ["
<< str_join(x_grad_dist_attr.multi_dims_mapping()) << "]";
return {{x_dist_attr_dst, out_grad_dist_attr_dst}, {x_grad_dist_attr}};
}
SpmdInfo TileGradInferSpmdDynamic(const DistMetaTensor& x,
const DistMetaTensor& out_grad,
const IntArray& repeat_times) {
return TileGradInferSpmd(x, out_grad, repeat_times.GetData());
}
} // namespace distributed
} // namespace phi