chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
proto_library(auto_parallel_proto SRCS auto_parallel.proto)
|
||||
|
||||
collect_srcs(
|
||||
core_srcs
|
||||
SRCS
|
||||
device_mesh.cc
|
||||
process_mesh.cc
|
||||
dist_attr.cc
|
||||
dist_mapper.cc
|
||||
dist_tensor.cc
|
||||
dist_meta_tensor.cc
|
||||
proto_helper.cc
|
||||
placement_types.cc
|
||||
inferspmd_utils.cc)
|
||||
|
||||
add_subdirectory(reshard)
|
||||
@@ -0,0 +1,183 @@
|
||||
/* 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. */
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package phi.distributed.auto_parallel;
|
||||
|
||||
// ProcessMesh is used to organize processes and like n-dimension array.
|
||||
message ProcessMeshProto {
|
||||
// The size of each dimension.
|
||||
repeated int64 shape = 1;
|
||||
|
||||
// These process ids are stored by a row-major way.
|
||||
// There are no duplicate process ids within one process mesh.
|
||||
repeated int64 process_ids = 2;
|
||||
|
||||
// The name of each dimension.
|
||||
repeated string dim_names = 3;
|
||||
|
||||
}
|
||||
|
||||
message MeshDimsProto {
|
||||
repeated int64 mesh_dims = 1;
|
||||
}
|
||||
|
||||
// This distributed attribute describes how to distribute the corresponding tensor,
|
||||
// and store any other information needed by auto parallel.
|
||||
message TensorDistAttrProto {
|
||||
// The process mesh where a tensor is distributed.
|
||||
optional ProcessMeshProto process_mesh = 1;
|
||||
|
||||
// The length of dims_mapping is same as the length of the tensor shape.
|
||||
// The i-th dimension of the tensor will be sharded by the dims_mapping[i]-th dimension
|
||||
// of the above process mesh. If dims_mapping[i] is -1, the i-th dimension of the tensor
|
||||
// will not be sharded. For example, given a tensor shape [2, 6, 12], a process mesh
|
||||
// shape [2, 3] and a dims_mapping [-1, 1, 0], each sharded tensor will have a shape [2, 2, 6].
|
||||
repeated MeshDimsProto dims_mapping = 2;
|
||||
|
||||
// The batch dimension of the corresponding tensor.
|
||||
optional int64 batch_dim = 3;
|
||||
|
||||
// If the dynamic_dims[i] is True, the i-th dimension of the corresponding tensor
|
||||
// is dynamic changed. Otherwise, the i-th dimension of the tensor is static determined.
|
||||
repeated bool dynamic_dims = 4;
|
||||
|
||||
// This field is used to distinguish vars which are in same process_mesh and in different vpp chunk
|
||||
optional int64 chunk_id = 5;
|
||||
}
|
||||
|
||||
// This distributed attribute describes how to distribute the corresponding operator,
|
||||
// and store any other information needed by auto parallel.
|
||||
message OperatorDistAttrProto {
|
||||
message TensorDistAttrMappingEntryProto {
|
||||
optional string name = 1;
|
||||
optional TensorDistAttrProto tensor_dist_attr = 2;
|
||||
}
|
||||
// The key of this map is the input tensor name and the value is the distributed attribute
|
||||
// of the input tensor required by this corresponding operator.
|
||||
// The distributed attribute of the actual tensor may be not the same as that within
|
||||
// the distributed attribute of the operator.
|
||||
repeated TensorDistAttrMappingEntryProto input_dist_attrs = 1;
|
||||
|
||||
// The key of this map is the output tensor name and the value is the distributed attribute
|
||||
// of the output tensor required by this corresponding operator.
|
||||
// The distributed attribute of the actual tensor may be not the same as that within
|
||||
// the distributed attribute of the operator.
|
||||
repeated TensorDistAttrMappingEntryProto output_dist_attrs = 2;
|
||||
|
||||
// The process mesh where a op is distributed.
|
||||
optional ProcessMeshProto process_mesh = 3;
|
||||
|
||||
// A operator ideally has a distributed operator which may have multiple distributed implementations.
|
||||
// This filed is usually same as the operator type. However, some operators such as the element-wise operators
|
||||
// may shared the same distributed operator, the field is use for this scenario.
|
||||
optional string impl_type = 4;
|
||||
|
||||
// This field tells which distributed implementations of this corresponding operator
|
||||
// will be selected for the actual computation.
|
||||
optional int64 impl_idx = 5;
|
||||
|
||||
// This field is used to distinguish ops which are in same process_mesh and in different vpp chunk
|
||||
optional int64 chunk_id = 6;
|
||||
}
|
||||
|
||||
// This proto describes the capability of one device such as the computation and memory.
|
||||
message DeviceCapabilityProto {
|
||||
optional double single_precision_flops = 1;
|
||||
|
||||
optional double double_precision_flops = 2;
|
||||
|
||||
optional double memory_size_in_bytes = 3;
|
||||
|
||||
optional double clock_rate_in_ghz = 4;
|
||||
}
|
||||
|
||||
// This proto represents a device.
|
||||
message DeviceProto {
|
||||
// The global id of this device within the cluster.
|
||||
optional int64 global_id = 1;
|
||||
|
||||
// The local id of this device within the machine.
|
||||
optional int64 local_id = 2;
|
||||
|
||||
// The id of the machine own this device.
|
||||
optional int64 machine_id = 3;
|
||||
|
||||
// The id of the machine has this device.
|
||||
optional string type = 4;
|
||||
|
||||
// The capability of this device.
|
||||
optional DeviceCapabilityProto capability = 5;
|
||||
}
|
||||
|
||||
// This proto describes the capability of the link between two devices.
|
||||
message LinkCapabilityProto {
|
||||
optional int64 bandwidth = 1; // Bytes/s
|
||||
optional int64 latency = 2;
|
||||
}
|
||||
|
||||
message LinkProto {
|
||||
// The global id of the source device.
|
||||
optional int64 source_id = 1;
|
||||
|
||||
// The global id of the source device.
|
||||
optional int64 target_id = 2;
|
||||
|
||||
// Represent the link type.
|
||||
optional string type = 3;
|
||||
|
||||
// The capability of this link.
|
||||
optional LinkCapabilityProto capability = 4;
|
||||
}
|
||||
|
||||
// DeviceMesh is used to organize devices and like n-dimension array.
|
||||
message DeviceMeshProto {
|
||||
// The global id of this mesh.
|
||||
optional string name = 1;
|
||||
|
||||
// The size of each dimension.
|
||||
repeated int64 shape = 2;
|
||||
|
||||
// These device ids are stored by a row-major way.
|
||||
// There are no duplicate device ids within one device mesh.
|
||||
repeated int64 device_ids = 3;
|
||||
|
||||
// The name of each dimension.
|
||||
repeated string dim_names = 4;
|
||||
|
||||
// The devices of this mesh.
|
||||
repeated DeviceProto devices = 5;
|
||||
|
||||
// The links are between devices.
|
||||
repeated LinkProto links = 6;
|
||||
}
|
||||
|
||||
// Record the mapping between the logical processes and the physical devices.
|
||||
message DistributedMapperProto {
|
||||
// The device meshes used by this distributed computation,
|
||||
// which may be shared by different multiple device meshes.
|
||||
repeated DeviceMeshProto device_meshes = 1;
|
||||
|
||||
message MapperEntryProto {
|
||||
optional int64 process_id = 1;
|
||||
optional string device_mesh_name = 2;
|
||||
repeated int64 device_ids = 3;
|
||||
}
|
||||
|
||||
// The mapping from process ids to device ids.
|
||||
// It is also possible for one process to use multiple devices.
|
||||
// It is possible for one device shared by multiple processes.
|
||||
repeated MapperEntryProto process_id_to_device_ids = 2;
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
/* 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. */
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/device_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/proto_helper.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
namespace phi::distributed::auto_parallel {
|
||||
|
||||
std::string DeviceCapability::to_string() const {
|
||||
std::string str;
|
||||
str += "{sflops: " + to_string_with_precision(single_precision_flops) + ", ";
|
||||
str += "dflops: " + to_string_with_precision(double_precision_flops) + ", ";
|
||||
str += "memory: " + to_string_with_precision(memory_size_in_bytes) + ", ";
|
||||
str += "rate: " + to_string_with_precision(clock_rate_in_ghz) + "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
DeviceCapability DeviceCapability::from_proto(
|
||||
const DeviceCapabilityProto &proto) {
|
||||
DeviceCapability capability;
|
||||
capability.single_precision_flops = proto.single_precision_flops();
|
||||
capability.double_precision_flops = proto.double_precision_flops();
|
||||
capability.memory_size_in_bytes = proto.memory_size_in_bytes();
|
||||
capability.clock_rate_in_ghz = proto.clock_rate_in_ghz();
|
||||
return capability;
|
||||
}
|
||||
|
||||
void DeviceCapability::to_proto(DeviceCapabilityProto *proto) const {
|
||||
proto->set_single_precision_flops(single_precision_flops);
|
||||
proto->set_double_precision_flops(double_precision_flops);
|
||||
proto->set_memory_size_in_bytes(memory_size_in_bytes);
|
||||
proto->set_clock_rate_in_ghz(clock_rate_in_ghz);
|
||||
}
|
||||
|
||||
std::string Device::to_string() const {
|
||||
std::string str = "{global_id: " + std::to_string(global_id_) + ", ";
|
||||
str += "local_id: " + std::to_string(local_id_) + ", ";
|
||||
str += "machine_id: " + std::to_string(machine_id_) + ", ";
|
||||
str += "type: " + type_ + ", ";
|
||||
str += "capability: " + capability_.to_string() + "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
Device Device::from_proto(const DeviceProto &proto) {
|
||||
Device device;
|
||||
device.global_id_ = proto.global_id();
|
||||
device.local_id_ = proto.local_id();
|
||||
device.machine_id_ = proto.machine_id();
|
||||
device.type_ = proto.type();
|
||||
device.capability_ = DeviceCapability::from_proto(proto.capability());
|
||||
return device;
|
||||
}
|
||||
|
||||
void Device::to_proto(DeviceProto *proto) const {
|
||||
proto->set_global_id(global_id_);
|
||||
proto->set_local_id(local_id_);
|
||||
proto->set_machine_id(machine_id_);
|
||||
proto->set_type(type_);
|
||||
proto->mutable_capability()->CopyFrom(distributed::to_proto(capability_));
|
||||
}
|
||||
|
||||
bool operator==(const Device &lhs, const Device &rhs) {
|
||||
if (lhs.global_id() != rhs.global_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.local_id() != rhs.local_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.machine_id() != rhs.machine_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.type() != rhs.type()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LinkCapability::to_string() const {
|
||||
std::string str;
|
||||
str += "{bandwidth: " + to_string_with_precision(bandwidth) + ",";
|
||||
str += "latency: " + to_string_with_precision(latency) + "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
LinkCapability LinkCapability::from_proto(const LinkCapabilityProto &proto) {
|
||||
LinkCapability capability;
|
||||
capability.bandwidth = proto.bandwidth();
|
||||
capability.latency = proto.latency();
|
||||
return capability;
|
||||
}
|
||||
|
||||
void LinkCapability::to_proto(LinkCapabilityProto *proto) const {
|
||||
proto->set_bandwidth(bandwidth);
|
||||
proto->set_latency(latency);
|
||||
}
|
||||
|
||||
std::string Link::to_string() const {
|
||||
std::string str = "{source_id:" + std::to_string(source_id_) + ",";
|
||||
str += "target_id:" + std::to_string(target_id_) + ",";
|
||||
str += "type:" + type_ + ",";
|
||||
str += "capability:" + capability_.to_string() + "}";
|
||||
return str;
|
||||
}
|
||||
|
||||
Link Link::from_proto(const LinkProto &proto) {
|
||||
Link link;
|
||||
link.source_id_ = proto.source_id();
|
||||
link.target_id_ = proto.target_id();
|
||||
link.type_ = proto.type();
|
||||
link.capability_ = LinkCapability::from_proto(proto.capability());
|
||||
return link;
|
||||
}
|
||||
|
||||
void Link::to_proto(LinkProto *proto) const {
|
||||
proto->set_source_id(source_id_);
|
||||
proto->set_target_id(target_id_);
|
||||
proto->set_type(type_);
|
||||
proto->mutable_capability()->CopyFrom(distributed::to_proto(capability_));
|
||||
}
|
||||
|
||||
bool operator==(const Link &lhs, const Link &rhs) {
|
||||
if (lhs.source_id() != rhs.source_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.target_id() != rhs.target_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.type() != rhs.type()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Machine::contains(int64_t device_id) const {
|
||||
if (devices_.count(device_id) == 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::add_device(const Device &device) {
|
||||
if (id() == -1) {
|
||||
set_id(device.machine_id());
|
||||
} else {
|
||||
PADDLE_ENFORCE_EQ(device.machine_id(),
|
||||
id(),
|
||||
errors::InvalidArgument(
|
||||
"The machine id [%d] of the device should be equal "
|
||||
"to this machine id [%d].",
|
||||
device.machine_id(),
|
||||
id_));
|
||||
}
|
||||
devices_[device.global_id()] = &device;
|
||||
}
|
||||
|
||||
void Machine::add_link(const Link &link) {
|
||||
PADDLE_ENFORCE_EQ(contains(link.source_id()),
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"The source device id of the added link [%s] "
|
||||
"cannot be found in the device_ids. Please add the "
|
||||
"source device before adding this link",
|
||||
std::to_string(link.source_id())));
|
||||
links_[link.source_id()][link.target_id()] = &link;
|
||||
}
|
||||
|
||||
std::string Machine::to_string() const {
|
||||
std::string str = "{devices: [";
|
||||
for (const auto &device : devices_) {
|
||||
str += device.second->to_string() + ", ";
|
||||
}
|
||||
str.replace(str.size() - 2, 2, "], ");
|
||||
|
||||
str += "links: [";
|
||||
for (const auto &item : links_) {
|
||||
str += "{";
|
||||
str += "source_id: " + std::to_string(item.first) + ", neighbors: [";
|
||||
for (const auto &link : item.second) {
|
||||
str += link.second->to_string() + ", ";
|
||||
}
|
||||
str.replace(str.size() - 2, 2, "]}, ");
|
||||
}
|
||||
str.replace(str.size() - 4, 4, "]}");
|
||||
return str;
|
||||
}
|
||||
|
||||
DeviceMesh::DeviceMesh(const std::string &name,
|
||||
const std::vector<int64_t> &shape,
|
||||
const std::vector<int64_t> &device_ids,
|
||||
const std::vector<std::string> &dim_names) {
|
||||
name_ = name;
|
||||
shape_ = shape;
|
||||
int64_t size = this->size();
|
||||
|
||||
PADDLE_ENFORCE_EQ(
|
||||
size,
|
||||
device_ids.size(),
|
||||
errors::InvalidArgument("The size %d of this device mesh must be "
|
||||
"equal to the size %d of its device ids.",
|
||||
size,
|
||||
device_ids.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
has_duplicates(device_ids),
|
||||
false,
|
||||
errors::InvalidArgument("The device ids [%s] must be unique.",
|
||||
str_join(device_ids)));
|
||||
device_ids_ = device_ids;
|
||||
|
||||
PADDLE_ENFORCE_EQ(
|
||||
shape_.size(),
|
||||
dim_names.size(),
|
||||
errors::InvalidArgument(
|
||||
"The size %d of mesh shape must be equal to the size %d "
|
||||
"of the dimension names.",
|
||||
shape_.size(),
|
||||
dim_names.size()));
|
||||
PADDLE_ENFORCE_EQ(has_duplicates(dim_names),
|
||||
false,
|
||||
errors::InvalidArgument(
|
||||
"The names [%s] of each dimension must be unique.",
|
||||
str_join(dim_names)));
|
||||
dim_names_ = dim_names;
|
||||
}
|
||||
|
||||
int64_t DeviceMesh::size() const {
|
||||
if (shape_.empty()) return 0;
|
||||
int64_t size = 1;
|
||||
for (const int64_t dim_size : shape_) size *= dim_size;
|
||||
return size;
|
||||
}
|
||||
|
||||
bool DeviceMesh::contains(int64_t device_id) const {
|
||||
auto result =
|
||||
std::find(std::begin(device_ids_), std::end(device_ids_), device_id);
|
||||
if (result != std::end(device_ids_)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceMesh::add_device(const Device &device) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
contains(device.global_id()),
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"The added device id [%s] cannot be found in the device_ids.",
|
||||
std::to_string(device.global_id())));
|
||||
// Operator [] will create a new object if it cannot find one.
|
||||
// So we add the default constructor for Device and Machine
|
||||
// to make sure the new object can be created.
|
||||
devices_[device.global_id()] = device;
|
||||
machines_[device.machine_id()].add_device(devices_[device.global_id()]);
|
||||
}
|
||||
|
||||
void DeviceMesh::add_link(const Link &link) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
contains(link.source_id()),
|
||||
true,
|
||||
errors::InvalidArgument("The source id of the added link [%s] "
|
||||
"cannot be found in the device_ids.",
|
||||
std::to_string(link.source_id())));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
contains(link.target_id()),
|
||||
true,
|
||||
errors::InvalidArgument("The source id of the added link [%s] "
|
||||
"cannot be found in the device_ids.",
|
||||
std::to_string(link.target_id())));
|
||||
// Operator [] will create a new object if it cannot find one.
|
||||
// So we add the default constructor for Device and Machine
|
||||
// to make sure the new object can be created.
|
||||
links_[link.source_id()][link.target_id()] = link;
|
||||
const Device &source_device = devices_[link.source_id()];
|
||||
machines_[source_device.machine_id()].add_link(
|
||||
links_[link.source_id()][link.target_id()]);
|
||||
}
|
||||
|
||||
std::string DeviceMesh::to_string() const {
|
||||
std::string mesh_str = "{name: " + name_ + ", ";
|
||||
mesh_str += "shape: [" + str_join(shape_) + "], ";
|
||||
mesh_str += "device_ids: [" + str_join(device_ids_) + "], ";
|
||||
mesh_str += "dim_names: [" + str_join(dim_names_) + "], ";
|
||||
mesh_str += "\ndevices: [\n";
|
||||
for (const auto &device : devices_) {
|
||||
mesh_str += " " + device.second.to_string() + ",\n";
|
||||
}
|
||||
mesh_str.replace(mesh_str.size() - 2, 2, "],");
|
||||
|
||||
mesh_str += "\nlinks: [\n";
|
||||
for (const auto &item : links_) {
|
||||
mesh_str += " {";
|
||||
mesh_str += "source_id: " + std::to_string(item.first) + ", neighbors: [";
|
||||
for (const auto &link : item.second) {
|
||||
mesh_str += link.second.to_string() + ", ";
|
||||
}
|
||||
mesh_str.replace(mesh_str.size() - 2, 2, "]},\n");
|
||||
}
|
||||
mesh_str.replace(mesh_str.size() - 4, 4, "]}");
|
||||
return mesh_str;
|
||||
}
|
||||
|
||||
DeviceMesh DeviceMesh::from_proto(const DeviceMeshProto &proto) {
|
||||
DeviceMesh mesh;
|
||||
|
||||
mesh.name_ = proto.name();
|
||||
|
||||
mesh.shape_.resize(proto.shape_size());
|
||||
for (int i = 0; i < proto.shape_size(); ++i) {
|
||||
mesh.shape_[i] = proto.shape(i);
|
||||
}
|
||||
|
||||
mesh.device_ids_.resize(proto.device_ids_size());
|
||||
for (int i = 0; i < proto.device_ids_size(); ++i) {
|
||||
mesh.device_ids_[i] = proto.device_ids(i);
|
||||
}
|
||||
|
||||
mesh.dim_names_.resize(proto.dim_names_size());
|
||||
for (int i = 0; i < proto.dim_names_size(); ++i) {
|
||||
mesh.dim_names_[i] = proto.dim_names(i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.devices_size(); ++i) {
|
||||
mesh.add_device(Device::from_proto(proto.devices(i)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.links_size(); ++i) {
|
||||
mesh.add_link(Link::from_proto(proto.links(i)));
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void DeviceMesh::to_proto(DeviceMeshProto *proto) const {
|
||||
proto->set_name(name_);
|
||||
|
||||
for (const auto &i : shape_) {
|
||||
proto->add_shape(i);
|
||||
}
|
||||
|
||||
for (const auto &i : device_ids_) {
|
||||
proto->add_device_ids(i);
|
||||
}
|
||||
|
||||
for (const auto &i : dim_names_) {
|
||||
proto->add_dim_names(i);
|
||||
}
|
||||
|
||||
for (const auto &device : devices_) {
|
||||
proto->mutable_devices()->Add()->CopyFrom(
|
||||
distributed::to_proto(device.second));
|
||||
}
|
||||
|
||||
for (const auto &neighbors : links_) {
|
||||
for (const auto &link : neighbors.second) {
|
||||
proto->mutable_links()->Add()->CopyFrom(
|
||||
distributed::to_proto(link.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const DeviceMesh &lhs, const DeviceMesh &rhs) {
|
||||
// Use the unique name to do the fast comparison
|
||||
if (lhs.name() != rhs.name()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed::auto_parallel
|
||||
@@ -0,0 +1,307 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
namespace auto_parallel {
|
||||
|
||||
class DeviceCapabilityProto;
|
||||
class DeviceProto;
|
||||
class LinkCapabilityProto;
|
||||
class LinkProto;
|
||||
class DeviceMeshProto;
|
||||
|
||||
struct PADDLE_API DeviceCapability {
|
||||
double single_precision_flops = 0.0;
|
||||
double double_precision_flops = 0.0;
|
||||
double memory_size_in_bytes = 0.0;
|
||||
double clock_rate_in_ghz = 0.0;
|
||||
|
||||
// DeviceCapability from_string(const std::string& str);
|
||||
std::string to_string() const;
|
||||
|
||||
static DeviceCapability from_proto(const DeviceCapabilityProto& proto);
|
||||
void to_proto(DeviceCapabilityProto* proto) const;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const DeviceCapability& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
class PADDLE_API Device {
|
||||
public:
|
||||
Device() = default;
|
||||
Device(int64_t global_id,
|
||||
int64_t local_id,
|
||||
int64_t machine_id,
|
||||
const std::string& type)
|
||||
: global_id_(global_id),
|
||||
local_id_(local_id),
|
||||
machine_id_(machine_id),
|
||||
type_(type) {}
|
||||
|
||||
int64_t global_id() const { return global_id_; }
|
||||
int64_t local_id() const { return local_id_; }
|
||||
int64_t machine_id() const { return machine_id_; }
|
||||
const std::string& type() const { return type_; }
|
||||
|
||||
const DeviceCapability& capability() const { return capability_; }
|
||||
void set_capability(const DeviceCapability& capability) {
|
||||
capability_ = capability;
|
||||
}
|
||||
|
||||
// Device from_string(const std::string& mesh_str);
|
||||
std::string to_string() const;
|
||||
|
||||
static Device from_proto(const DeviceProto& proto);
|
||||
void to_proto(DeviceProto* proto) const;
|
||||
|
||||
private:
|
||||
int64_t global_id_;
|
||||
int64_t local_id_;
|
||||
int64_t machine_id_;
|
||||
std::string type_;
|
||||
DeviceCapability capability_;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const Device& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
PADDLE_API bool operator==(const Device& lhs, const Device& rhs);
|
||||
|
||||
inline bool operator!=(const Device& lhs, const Device& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
struct PADDLE_API LinkCapability {
|
||||
int64_t bandwidth = 0.0; // Bytes/s
|
||||
int64_t latency = 0.0;
|
||||
|
||||
// LinkCapability from_string(const std::string& str);
|
||||
std::string to_string() const;
|
||||
|
||||
static LinkCapability from_proto(const LinkCapabilityProto& proto);
|
||||
void to_proto(LinkCapabilityProto* proto) const;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const LinkCapability& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
class PADDLE_API Link {
|
||||
public:
|
||||
Link() = default;
|
||||
|
||||
Link(int64_t source_id, int64_t target_id, const std::string& type)
|
||||
: source_id_(source_id), target_id_(target_id), type_(type) {}
|
||||
|
||||
int64_t source_id() const { return source_id_; }
|
||||
int64_t target_id() const { return target_id_; }
|
||||
const std::string& type() const { return type_; }
|
||||
|
||||
const LinkCapability& capability() const { return capability_; }
|
||||
void set_capability(const LinkCapability& capability) {
|
||||
capability_ = capability;
|
||||
}
|
||||
|
||||
// Link from_string(const std::string& str);
|
||||
std::string to_string() const;
|
||||
|
||||
static Link from_proto(const LinkProto& proto);
|
||||
void to_proto(LinkProto* proto) const;
|
||||
|
||||
private:
|
||||
int64_t source_id_;
|
||||
int64_t target_id_;
|
||||
std::string type_;
|
||||
LinkCapability capability_;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const Link& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
PADDLE_API bool operator==(const Link& lhs, const Link& rhs);
|
||||
|
||||
inline bool operator!=(const Link& lhs, const Link& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
class PADDLE_API Machine {
|
||||
public:
|
||||
Machine() = default;
|
||||
|
||||
explicit Machine(int64_t id) : id_(id) {}
|
||||
|
||||
int64_t id() const { return id_; }
|
||||
|
||||
void set_id(int64_t id) { id_ = id; }
|
||||
|
||||
const std::unordered_map<int64_t, const Device*>& devices() const {
|
||||
return devices_;
|
||||
}
|
||||
|
||||
const std::unordered_map<int64_t, std::unordered_map<int64_t, const Link*>>&
|
||||
links() const {
|
||||
return links_;
|
||||
}
|
||||
|
||||
const Device& device(int64_t global_id) const {
|
||||
return *devices_.at(global_id);
|
||||
}
|
||||
|
||||
const Link& link(int64_t source_id, int64_t target_id) const {
|
||||
return *links_.at(source_id).at(target_id);
|
||||
}
|
||||
|
||||
bool contains(int64_t device_id) const;
|
||||
|
||||
void add_device(const Device& device);
|
||||
|
||||
void add_link(const Link& link);
|
||||
|
||||
// Machine from_string(const std::string& str);
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
int64_t id_ = -1;
|
||||
std::unordered_map<int64_t, const Device*> devices_;
|
||||
std::unordered_map<int64_t, std::unordered_map<int64_t, const Link*>> links_;
|
||||
};
|
||||
|
||||
class PADDLE_API DeviceMesh {
|
||||
public:
|
||||
DeviceMesh() = default;
|
||||
|
||||
DeviceMesh(const std::string& name,
|
||||
const std::vector<int64_t>& shape,
|
||||
const std::vector<int64_t>& device_ids,
|
||||
const std::vector<std::string>& dim_names);
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
void set_name(const std::string& name) { name_ = name; }
|
||||
|
||||
const std::vector<int64_t>& shape() const { return shape_; }
|
||||
|
||||
const std::vector<int64_t>& device_ids() const { return device_ids_; }
|
||||
|
||||
const std::vector<std::string>& dim_names() const { return dim_names_; }
|
||||
|
||||
std::string device_type() const {
|
||||
if (empty()) return "UNKNOWN";
|
||||
if (devices_.empty())
|
||||
return "UNKNOWN";
|
||||
else
|
||||
return std::begin(devices_)->second.type();
|
||||
}
|
||||
|
||||
const std::unordered_map<int64_t, Device>& devices() const {
|
||||
return devices_;
|
||||
}
|
||||
|
||||
const std::unordered_map<int64_t, std::unordered_map<int64_t, Link>>& links()
|
||||
const {
|
||||
return links_;
|
||||
}
|
||||
|
||||
const std::unordered_map<int64_t, Machine>& machines() const {
|
||||
return machines_;
|
||||
}
|
||||
|
||||
const Device& device(int64_t global_id) const {
|
||||
return devices_.at(global_id);
|
||||
}
|
||||
|
||||
const Link& link(int64_t source_id, int64_t target_id) const {
|
||||
return links_.at(source_id).at(target_id);
|
||||
}
|
||||
|
||||
const Machine& machine(int64_t machine_id) const {
|
||||
return machines_.at(machine_id);
|
||||
}
|
||||
|
||||
int64_t size() const;
|
||||
int64_t ndim() const { return shape_.size(); }
|
||||
|
||||
int64_t dim_size(int64_t dim) const {
|
||||
int64_t cdim = canonical_dim(dim, shape_.size());
|
||||
return shape_[cdim];
|
||||
}
|
||||
|
||||
int64_t dim_size(const std::string& dim_name) const {
|
||||
for (std::size_t i = 0; i < dim_names_.size(); ++i) {
|
||||
if (dim_names_[i] == dim_name) {
|
||||
return shape_[i];
|
||||
}
|
||||
}
|
||||
PADDLE_THROW(errors::InvalidArgument(
|
||||
"Cannot find the dimension of %s in this device mesh.", dim_name));
|
||||
}
|
||||
|
||||
bool empty() const { return (shape_.empty() || device_ids_.empty()); }
|
||||
bool contains(int64_t device_id) const;
|
||||
|
||||
void add_device(const Device& device);
|
||||
void add_link(const Link& link);
|
||||
|
||||
// DeviceMesh from_string(const std::string& mesh_str);
|
||||
std::string to_string() const;
|
||||
|
||||
static DeviceMesh from_proto(const DeviceMeshProto& proto);
|
||||
void to_proto(DeviceMeshProto* proto) const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<int64_t> shape_;
|
||||
std::vector<int64_t> device_ids_;
|
||||
std::vector<std::string> dim_names_;
|
||||
std::unordered_map<int64_t, Device> devices_;
|
||||
std::unordered_map<int64_t, std::unordered_map<int64_t, Link>> links_;
|
||||
std::unordered_map<int64_t, Machine> machines_;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const DeviceMesh& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
PADDLE_API bool operator==(const DeviceMesh& lhs, const DeviceMesh& rhs);
|
||||
|
||||
inline bool operator!=(const DeviceMesh& lhs, const DeviceMesh& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace auto_parallel
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,605 @@
|
||||
/* 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. */
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/proto_helper.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
using phi::distributed::auto_parallel::str_join;
|
||||
using phi::distributed::auto_parallel::TensorDistAttrProto;
|
||||
|
||||
// partial is not allow annotated by user by now.
|
||||
std::vector<std::string> TensorDistAttr::fields_{
|
||||
"process_mesh", "dims_mapping", "batch_dim", "chunk_id", "dynamic_dims"};
|
||||
|
||||
TensorDistAttr::TensorDistAttr(const std::vector<int64_t>& tensor_shape) {
|
||||
set_default_dims_mapping(tensor_shape);
|
||||
set_default_dynamic_dims(tensor_shape);
|
||||
}
|
||||
|
||||
TensorDistAttr::TensorDistAttr(const TensorDistAttr& dist_attr) {
|
||||
copy_from(dist_attr);
|
||||
}
|
||||
|
||||
TensorDistAttr& TensorDistAttr::operator=(const TensorDistAttr& dist_attr) {
|
||||
if (this == &dist_attr) return *this;
|
||||
TensorDistAttr tmp(dist_attr);
|
||||
std::swap(this->process_mesh_, tmp.process_mesh_);
|
||||
std::swap(this->dims_mapping_, tmp.dims_mapping_);
|
||||
std::swap(this->batch_dim_, tmp.batch_dim_);
|
||||
std::swap(this->chunk_id_, tmp.chunk_id_);
|
||||
std::swap(this->dynamic_dims_, tmp.dynamic_dims_);
|
||||
std::swap(this->annotated_, tmp.annotated_);
|
||||
std::swap(this->partial_status_, tmp.partial_status_);
|
||||
std::swap(this->skip_check_mesh_, tmp.skip_check_mesh_);
|
||||
std::swap(this->split_factor_map_, tmp.split_factor_map_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TensorDistAttr::copy_from(const TensorDistAttr& dist_attr) {
|
||||
set_process_mesh(dist_attr.process_mesh());
|
||||
set_dims_mapping(dist_attr.multi_dims_mapping());
|
||||
split_factor_map_ = dist_attr.split_factor();
|
||||
set_batch_dim(dist_attr.batch_dim());
|
||||
set_chunk_id(dist_attr.chunk_id());
|
||||
set_dynamic_dims(dist_attr.dynamic_dims());
|
||||
set_annotated(dist_attr.annotated());
|
||||
set_partial_status(dist_attr.partial_status());
|
||||
skip_check_mesh_ = dist_attr.skip_check_mesh();
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_process_mesh(const ProcessMesh& process_mesh) {
|
||||
process_mesh_ = process_mesh;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_dims_mapping(
|
||||
const std::vector<int64_t>& dims_mapping) {
|
||||
dims_mapping_proxy = dims_mapping;
|
||||
// dynamic_dims_ and dims_mapping may be not consistent
|
||||
if (dynamic_dims_.empty() || dims_mapping.empty()) {
|
||||
set_default_dynamic_dims(dims_mapping);
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_dims_mapping(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping) {
|
||||
dims_mapping_proxy = dims_mapping;
|
||||
// dynamic_dims_ and dims_mapping may be not consistent
|
||||
if (dynamic_dims_.empty() || dims_mapping.empty()) {
|
||||
set_default_dynamic_dims(std::vector<int64_t>(dims_mapping.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_batch_dim(int64_t batch_dim) {
|
||||
batch_dim_ = batch_dim;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_chunk_id(const int64_t& chunk_id) {
|
||||
chunk_id_ = chunk_id;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_dynamic_dims(const std::vector<bool>& dynamic_dims) {
|
||||
dynamic_dims_ = dynamic_dims;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_annotated(
|
||||
const std::map<std::string, bool>& annotated) {
|
||||
annotated_ = annotated;
|
||||
}
|
||||
|
||||
int64_t TensorDistAttr::get_split_factor(int64_t mesh_dim) const {
|
||||
PADDLE_ENFORCE_LT(mesh_dim,
|
||||
process_mesh_.ndim(),
|
||||
::common::errors::InvalidArgument(
|
||||
"Expected mesh dim is less than process mesh dim size, "
|
||||
"but got mesh dim %d, Process mesh ndim is %d",
|
||||
mesh_dim,
|
||||
process_mesh_.ndim()));
|
||||
return split_factor_map_.get_split_factor(mesh_dim);
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_split_factor(int64_t mesh_dim, int64_t split_factor) {
|
||||
split_factor_map_.set_split_factor(mesh_dim, split_factor);
|
||||
}
|
||||
|
||||
void TensorDistAttr::clear_split_factor(int64_t mesh_dim) {
|
||||
split_factor_map_.clear_split_factor(mesh_dim);
|
||||
}
|
||||
|
||||
const std::set<int64_t> TensorDistAttr::partial_dims() const {
|
||||
std::set<int64_t> keys;
|
||||
for (auto& kv : partial_status_) {
|
||||
keys.emplace(kv.first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_partial_status(
|
||||
const paddle::flat_hash_map<int64_t, ReduceType>& partial_status) {
|
||||
partial_status_ = partial_status;
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_partial_status(const std::vector<int64_t>& dims,
|
||||
const ReduceType& type) {
|
||||
for (const auto& dim : dims) {
|
||||
if (is_partial(dim)) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Trying to Set dim %d as Partial which is already a Partial dim.",
|
||||
dim));
|
||||
}
|
||||
if (is_shard(dim)) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Trying to Set dim %d as Partial which is a Sharding dim.", dim));
|
||||
}
|
||||
partial_status_.emplace(dim, type);
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::clean_partial_status() { partial_status_.clear(); }
|
||||
|
||||
void TensorDistAttr::clean_partial_dims(const std::vector<int64_t>& dims) {
|
||||
for (const auto& dim : dims) {
|
||||
if (partial_status_.count(dim) == 0) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Trying to clean Partial on dim %d but it is not Partial.", dim));
|
||||
} else {
|
||||
partial_status_.erase(dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_default_dims_mapping(
|
||||
const std::vector<int64_t>& tensor_shape) {
|
||||
if (!tensor_shape.empty()) {
|
||||
dims_mapping_proxy = std::vector<std::vector<int64_t>>(tensor_shape.size());
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_default_dynamic_dims(
|
||||
const std::vector<int64_t>& tensor_shape) {
|
||||
dynamic_dims_ = std::vector<bool>(tensor_shape.size(), false);
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_default_dynamic_dims(int64_t tensor_shape_size) {
|
||||
dynamic_dims_ = std::vector<bool>(tensor_shape_size, false);
|
||||
}
|
||||
|
||||
void TensorDistAttr::mark_annotated(const std::string& name) {
|
||||
auto result = std::find(std::begin(fields_), std::end(fields_), name);
|
||||
if (result != std::end(fields_)) {
|
||||
annotated_[name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_process_mesh(
|
||||
const ProcessMesh& process_mesh) const {
|
||||
VLOG(4) << "[TensorDistAttr verify_process_mesh] "
|
||||
<< process_mesh.to_string();
|
||||
if (!process_mesh_.empty()) {
|
||||
for (const auto& dims_mapping : dims_mapping_) {
|
||||
for (int64_t mesh_dim : dims_mapping)
|
||||
if (mesh_dim >= process_mesh_.ndim()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_dims_mapping(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping,
|
||||
const std::vector<int64_t>& tensor_shape) const {
|
||||
VLOG(4) << "[TensorDistAttr verify_dims_mapping] " << str_join(dims_mapping);
|
||||
if (dims_mapping.size() != tensor_shape.size()) {
|
||||
return false;
|
||||
}
|
||||
std::unordered_map<int64_t, int64_t> map;
|
||||
if (!process_mesh_.empty()) {
|
||||
for (const auto& mesh_dims : dims_mapping) {
|
||||
for (int64_t mesh_dim : mesh_dims) {
|
||||
if (mesh_dim >= process_mesh_.ndim()) {
|
||||
return false;
|
||||
}
|
||||
++map[mesh_dim];
|
||||
if (map[mesh_dim] > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& mesh_dims : dims_mapping) {
|
||||
for (int64_t dim : mesh_dims) {
|
||||
++map[dim];
|
||||
if (map[dim] > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_batch_dim(
|
||||
int64_t dim, const std::vector<int64_t>& tensor_shape) const {
|
||||
VLOG(4) << "[TensorDistAttr verify_batch_dim] " << dim;
|
||||
int64_t ndim = static_cast<int64_t>(tensor_shape.size());
|
||||
if (ndim > 0) {
|
||||
if (dim < 0) {
|
||||
dim = dim + ndim;
|
||||
}
|
||||
if (dim < 0 || dim >= ndim) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_dynamic_dims(
|
||||
const std::vector<bool>& dynamic_dims,
|
||||
const std::vector<int64_t>& tensor_shape) const {
|
||||
VLOG(4) << "[TensorDistAttr verify_dynamic_dims] " << str_join(dynamic_dims);
|
||||
if (!dynamic_dims.empty() && dynamic_dims.size() != tensor_shape.size()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_annotated(
|
||||
const std::map<std::string, bool>& annotated) const {
|
||||
VLOG(4) << "[TensorDistAttr verify_annotated] " << str_join(annotated);
|
||||
for (const auto& item : annotated) {
|
||||
auto result = std::find(std::begin(fields_), std::end(fields_), item.first);
|
||||
if (result == std::end(fields_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_partial_status() const {
|
||||
VLOG(4) << "[TensorDistAttr verify_partial_status] "
|
||||
<< partial_status_string();
|
||||
for (auto& itr : partial_status_) {
|
||||
if (itr.first < 0 || itr.first >= process_mesh_.ndim()) {
|
||||
return false;
|
||||
}
|
||||
if (itr.second < ReduceType::kRedSum || itr.second > ReduceType::kRedAll) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify(const std::vector<int64_t>& tensor_shape) const {
|
||||
if (!verify_process_mesh(process_mesh_)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_dims_mapping(dims_mapping_, tensor_shape)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_batch_dim(batch_dim_, tensor_shape)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_dynamic_dims(dynamic_dims_, tensor_shape)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_annotated(annotated_)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_partial_status()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::verify_dynamic(
|
||||
const std::vector<int64_t>& tensor_shape) const {
|
||||
if (!verify_process_mesh(process_mesh_)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_dims_mapping(dims_mapping_, tensor_shape)) {
|
||||
return false;
|
||||
}
|
||||
if (!verify_partial_status()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TensorDistAttr::to_string() const {
|
||||
std::string dist_str;
|
||||
dist_str += "{process_mesh: " + process_mesh_.to_string() + ", ";
|
||||
dist_str += "dim_mappings: [" + str_join(dims_mapping_) + "], ";
|
||||
dist_str += "batch_dim: " + std::to_string(batch_dim_) + ", ";
|
||||
dist_str += "chunk_id: " + std::to_string(chunk_id_) + ", ";
|
||||
dist_str += "skip_check_mesh: " + std::to_string(skip_check_mesh_) + ", ";
|
||||
dist_str += "dynamic_dims: [" + str_join(dynamic_dims_) + "], ";
|
||||
dist_str += "annotated: [" + str_join(annotated_) + "], ";
|
||||
dist_str += "partial: " + partial_status_string() + ", ";
|
||||
dist_str += "split_factor: " + split_factor_map_.to_string() + ".}";
|
||||
return dist_str;
|
||||
}
|
||||
|
||||
void TensorDistAttr::from_proto(const TensorDistAttrProto& proto) {
|
||||
process_mesh_ = ProcessMesh::from_proto(proto.process_mesh());
|
||||
dims_mapping_.resize(proto.dims_mapping_size());
|
||||
|
||||
for (int i = 0; i < proto.dims_mapping_size(); ++i) {
|
||||
const auto& mesh_dims_proto = proto.dims_mapping(i);
|
||||
std::vector<int64_t> mesh_dims_vec;
|
||||
for (int j = 0; j < mesh_dims_proto.mesh_dims_size(); ++j) {
|
||||
mesh_dims_vec.push_back(mesh_dims_proto.mesh_dims(j));
|
||||
}
|
||||
dims_mapping_[i] = mesh_dims_vec;
|
||||
}
|
||||
|
||||
batch_dim_ = proto.batch_dim();
|
||||
chunk_id_ = proto.chunk_id();
|
||||
dynamic_dims_.resize(proto.dynamic_dims_size());
|
||||
for (int i = 0; i < proto.dynamic_dims_size(); ++i) {
|
||||
dynamic_dims_[i] = proto.dynamic_dims(i);
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::to_proto(TensorDistAttrProto* proto) const {
|
||||
proto->mutable_process_mesh()->CopyFrom(distributed::to_proto(process_mesh_));
|
||||
|
||||
for (size_t i = 0; i < dims_mapping_.size(); ++i) {
|
||||
proto->add_dims_mapping();
|
||||
for (size_t j = 0; j < dims_mapping_.at(i).size(); ++j) {
|
||||
proto->mutable_dims_mapping(i)->add_mesh_dims(dims_mapping_[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
proto->set_batch_dim(batch_dim_);
|
||||
proto->set_chunk_id(chunk_id_);
|
||||
for (const auto& i : dynamic_dims_) {
|
||||
proto->add_dynamic_dims(i);
|
||||
}
|
||||
}
|
||||
|
||||
std::string TensorDistAttr::serialize_to_string() {
|
||||
std::string data;
|
||||
auto proto = distributed::to_proto(*this);
|
||||
proto.SerializeToString(&data);
|
||||
PADDLE_ENFORCE_EQ(distributed::to_proto(*this).SerializeToString(&data),
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"Failed to serialize tensor dist attr to string."));
|
||||
return data;
|
||||
}
|
||||
|
||||
void TensorDistAttr::parse_from_string(const std::string& data) {
|
||||
TensorDistAttrProto proto;
|
||||
PADDLE_ENFORCE_EQ(
|
||||
proto.ParseFromString(data),
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"Failed to parse tensor dist attr from string: %s.", data));
|
||||
from_proto(proto);
|
||||
}
|
||||
|
||||
bool operator==(const TensorDistAttr& lhs, const TensorDistAttr& rhs) {
|
||||
if (lhs.process_mesh() != rhs.process_mesh()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.multi_dims_mapping() != rhs.multi_dims_mapping()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.batch_dim() != rhs.batch_dim()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.chunk_id() != rhs.chunk_id()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.dynamic_dims() != rhs.dynamic_dims()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.partial_status() != rhs.partial_status()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.split_factor() != rhs.split_factor()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TensorDistAttr::partial_status_string() const {
|
||||
std::string partial_status_str = "[";
|
||||
for (auto& itr : partial_status_) {
|
||||
partial_status_str += "Partial(dims:" + std::to_string(itr.first) + ", " +
|
||||
ReduceTypeStrings[static_cast<int>(itr.second)] +
|
||||
"), ";
|
||||
}
|
||||
partial_status_str += "]";
|
||||
return partial_status_str;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::empty() const {
|
||||
// dims_mapping is empty when the tensor is 0-dim, but it is also be valid.
|
||||
return process_mesh_.empty();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<PlacementStatus>> TensorDistAttr::to_placement()
|
||||
const {
|
||||
auto ndim = process_mesh_.ndim();
|
||||
std::vector<std::shared_ptr<PlacementStatus>> placement(
|
||||
ndim, std::make_shared<ReplicatedStatus>());
|
||||
for (size_t i = 0; i < dims_mapping_.size(); ++i) {
|
||||
const auto& cur_dims = dims_mapping_.at(i);
|
||||
for (size_t j = 0; j < cur_dims.size(); ++j) {
|
||||
int64_t cur_dim = cur_dims.at(j);
|
||||
PADDLE_ENFORCE_LT(
|
||||
cur_dim,
|
||||
ndim,
|
||||
errors::InvalidArgument(
|
||||
"Split axis %ld can not exceed the ndim of process_mesh %ld",
|
||||
cur_dim,
|
||||
ndim));
|
||||
placement[cur_dim] = std::make_shared<ShardStatus>(i, j);
|
||||
}
|
||||
}
|
||||
for (auto& itr : partial_status_) {
|
||||
PADDLE_ENFORCE_LT(
|
||||
itr.first,
|
||||
ndim,
|
||||
errors::InvalidArgument(
|
||||
"Partial axis %ld can not exceed the ndim of process_mesh %ld",
|
||||
itr.first,
|
||||
ndim));
|
||||
placement[itr.first] = std::make_shared<PartialStatus>(itr.second);
|
||||
}
|
||||
return placement;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::is_replicated(int64_t mesh_axis) const {
|
||||
auto placement = ToPlacements(*this);
|
||||
if (mesh_axis == -1) {
|
||||
return std::all_of(
|
||||
placement.begin(), placement.end(), [](std::shared_ptr<Placement> p) {
|
||||
return p->is_replicated();
|
||||
});
|
||||
} else {
|
||||
return placement[mesh_axis]->is_replicated();
|
||||
}
|
||||
}
|
||||
|
||||
bool TensorDistAttr::is_shard(int64_t mesh_axis,
|
||||
std::optional<int64_t> tensor_axis) const {
|
||||
auto placement = ToPlacements(*this);
|
||||
if (mesh_axis == -1) {
|
||||
return std::any_of(placement.begin(),
|
||||
placement.end(),
|
||||
[tensor_axis](std::shared_ptr<Placement> p) {
|
||||
return p->is_shard(tensor_axis);
|
||||
});
|
||||
} else {
|
||||
return placement[mesh_axis]->is_shard(tensor_axis);
|
||||
}
|
||||
}
|
||||
|
||||
bool TensorDistAttr::is_partial(int64_t mesh_axis) const {
|
||||
if (mesh_axis == -1) {
|
||||
return !partial_status_.empty();
|
||||
} else {
|
||||
return partial_status_.count(mesh_axis) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::set_skip_check_mesh(bool skip) { skip_check_mesh_ = skip; }
|
||||
|
||||
bool TensorDistAttr::is_co_shard() const {
|
||||
for (const auto& mesh_dims : dims_mapping_) {
|
||||
if (mesh_dims.size() > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TensorDistAttr::DimMapProxy& TensorDistAttr::DimMapProxy::operator=(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping) {
|
||||
dims_mapping_2d->resize(dims_mapping.size());
|
||||
*dims_mapping_2d = dims_mapping;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorDistAttr::DimMapProxy& TensorDistAttr::DimMapProxy::operator=(
|
||||
const std::vector<int64_t>& dims_mapping) {
|
||||
dims_mapping_1d = dims_mapping;
|
||||
sync_2d_map();
|
||||
VLOG(4) << "Set 1d dims_mapping, Sync 2d. 1d "
|
||||
<< auto_parallel::str_join(dims_mapping_1d) << " , 2d "
|
||||
<< auto_parallel::str_join(*dims_mapping_2d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TensorDistAttr::DimMapProxy& TensorDistAttr::DimMapProxy::operator=(
|
||||
const DimMapProxy& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
*dims_mapping_2d = (*other.dims_mapping_2d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool TensorDistAttr::DimMapProxy::operator==(const DimMapProxy& other) {
|
||||
return (*dims_mapping_2d) == (*other.dims_mapping_2d);
|
||||
}
|
||||
|
||||
bool TensorDistAttr::DimMapProxy::operator!=(const DimMapProxy& other) {
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
TensorDistAttr::DimMapProxy::operator const std::vector<std::vector<int64_t>>&()
|
||||
const {
|
||||
return *dims_mapping_2d;
|
||||
}
|
||||
|
||||
TensorDistAttr::DimMapProxy::operator const std::vector<int64_t>&() const {
|
||||
sync_1d_map();
|
||||
VLOG(4) << "Get 1d dims_mapping, Sync 1d. 1d is "
|
||||
<< auto_parallel::str_join(dims_mapping_1d) << ", 2d is "
|
||||
<< auto_parallel::str_join(*dims_mapping_2d);
|
||||
return dims_mapping_1d;
|
||||
}
|
||||
|
||||
void TensorDistAttr::DimMapProxy::sync_1d_map() const {
|
||||
dims_mapping_1d.resize(dims_mapping_2d->size());
|
||||
for (size_t i = 0; i < dims_mapping_2d->size(); ++i) {
|
||||
size_t num_mesh_dim = dims_mapping_2d->at(i).size();
|
||||
if (num_mesh_dim <= 1) {
|
||||
dims_mapping_1d[i] =
|
||||
(*dims_mapping_2d)[i].empty() ? -1 : (*dims_mapping_2d)[i][0];
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t max_mesh_dim = (*dims_mapping_2d)[i][0];
|
||||
int64_t max_mesh_dim_size = process_mesh.shape()[max_mesh_dim];
|
||||
|
||||
for (size_t j = 1; j < num_mesh_dim; ++j) {
|
||||
int64_t cur_mesh_dim = (*dims_mapping_2d)[i][j];
|
||||
int64_t cur_mesh_dim_size = process_mesh.shape()[cur_mesh_dim];
|
||||
|
||||
if (cur_mesh_dim_size > max_mesh_dim_size) {
|
||||
max_mesh_dim = cur_mesh_dim;
|
||||
max_mesh_dim_size = cur_mesh_dim_size;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(WARNING) << "There are " << num_mesh_dim << " shared on tensor dim "
|
||||
<< i << ". Now fallback to sharding by mesh dim "
|
||||
<< max_mesh_dim << ".";
|
||||
|
||||
dims_mapping_1d[i] = max_mesh_dim;
|
||||
}
|
||||
}
|
||||
|
||||
void TensorDistAttr::DimMapProxy::sync_2d_map() {
|
||||
dims_mapping_2d->resize(dims_mapping_1d.size());
|
||||
for (size_t i = 0; i < dims_mapping_1d.size(); ++i) {
|
||||
if (dims_mapping_1d.at(i) == -1) {
|
||||
(*dims_mapping_2d)[i] = {};
|
||||
} else {
|
||||
(*dims_mapping_2d)[i] = {dims_mapping_1d[i]};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,289 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/common/reduce_type.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/utils/flat_hash_map.h"
|
||||
#include "paddle/utils/test_macros.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
namespace auto_parallel {
|
||||
class TensorDistAttrProto;
|
||||
}
|
||||
|
||||
constexpr int kReplicateDim = -1;
|
||||
|
||||
class PlacementStatus {
|
||||
public:
|
||||
virtual ~PlacementStatus() = default;
|
||||
|
||||
virtual bool is_shard(int64_t axis = -1) const { return false; }
|
||||
virtual bool is_partial() const { return false; }
|
||||
virtual bool is_replicated() const { return false; }
|
||||
};
|
||||
|
||||
class ReplicatedStatus final : public PlacementStatus {
|
||||
public:
|
||||
bool is_replicated() const override { return true; }
|
||||
};
|
||||
|
||||
class PartialStatus final : public PlacementStatus {
|
||||
public:
|
||||
PartialStatus(ReduceType type) : type_(type) {}
|
||||
bool is_partial() const override { return true; }
|
||||
ReduceType get_reduce_type() const { return type_; }
|
||||
|
||||
private:
|
||||
ReduceType type_{ReduceType::kRedSum};
|
||||
};
|
||||
|
||||
class ShardStatus final : public PlacementStatus {
|
||||
public:
|
||||
explicit ShardStatus(int64_t axis) : axis_(axis) {}
|
||||
ShardStatus(int64_t axis, int64_t co_shard_order)
|
||||
: axis_(axis), co_shard_order_(co_shard_order) {}
|
||||
bool is_shard(int64_t axis = -1) const override {
|
||||
if (axis == -1) {
|
||||
return true;
|
||||
} else {
|
||||
return axis == axis_;
|
||||
}
|
||||
}
|
||||
int64_t get_axis() const { return axis_; }
|
||||
|
||||
private:
|
||||
int64_t axis_{-1};
|
||||
int64_t co_shard_order_{0};
|
||||
};
|
||||
|
||||
class PADDLE_API TensorDistAttr {
|
||||
public:
|
||||
TensorDistAttr() = default;
|
||||
|
||||
explicit TensorDistAttr(const std::vector<int64_t>& tensor_shape);
|
||||
|
||||
TensorDistAttr(const TensorDistAttr& dist_attr);
|
||||
|
||||
TensorDistAttr& operator=(const TensorDistAttr& dist_attr);
|
||||
|
||||
void copy_from(const TensorDistAttr& dist_attr);
|
||||
|
||||
const ProcessMesh& process_mesh() const { return process_mesh_; }
|
||||
|
||||
void set_process_mesh(const ProcessMesh& process_mesh);
|
||||
|
||||
const std::vector<int64_t>& dims_mapping() const {
|
||||
return dims_mapping_proxy;
|
||||
}
|
||||
|
||||
const std::vector<std::vector<int64_t>>& multi_dims_mapping() const {
|
||||
return dims_mapping_proxy;
|
||||
}
|
||||
|
||||
void set_dims_mapping(const std::vector<int64_t>& dims_mapping);
|
||||
|
||||
void set_dims_mapping(const std::vector<std::vector<int64_t>>& dims_mapping);
|
||||
|
||||
const auto_parallel::SplitFactor& split_factor() const {
|
||||
return split_factor_map_;
|
||||
}
|
||||
|
||||
int64_t get_split_factor(int64_t mesh_dim) const;
|
||||
|
||||
void set_split_factor(int64_t mesh_dim, int64_t split_factor);
|
||||
|
||||
void clear_split_factor(int64_t mesh_dim);
|
||||
|
||||
// return vector of mesh dims on which the this tensor is partial on
|
||||
const std::set<int64_t> partial_dims() const;
|
||||
|
||||
const paddle::flat_hash_map<int64_t, ReduceType>& partial_status() const {
|
||||
return partial_status_;
|
||||
}
|
||||
|
||||
// by map
|
||||
void set_partial_status(
|
||||
const paddle::flat_hash_map<int64_t, ReduceType>& partial_status);
|
||||
|
||||
// by each dim
|
||||
void set_partial_status(const std::vector<int64_t>& dims,
|
||||
const ReduceType& type = ReduceType::kRedSum);
|
||||
// all
|
||||
void clean_partial_status();
|
||||
|
||||
// clean by dims
|
||||
void clean_partial_dims(const std::vector<int64_t>& dims);
|
||||
|
||||
void set_default_dims_mapping(const std::vector<int64_t>& tensor_shape);
|
||||
|
||||
int64_t batch_dim() const { return batch_dim_; }
|
||||
|
||||
void set_batch_dim(int64_t batch_dim);
|
||||
|
||||
const int64_t& chunk_id() const { return chunk_id_; }
|
||||
|
||||
void set_chunk_id(const int64_t& chunk_id);
|
||||
|
||||
const std::vector<bool>& dynamic_dims() const { return dynamic_dims_; }
|
||||
|
||||
void set_dynamic_dims(const std::vector<bool>& dynamic_dims);
|
||||
|
||||
void set_default_dynamic_dims(const std::vector<int64_t>& tensor_shape);
|
||||
|
||||
void set_default_dynamic_dims(int64_t tensor_shape_size);
|
||||
|
||||
const std::map<std::string, bool>& annotated() const { return annotated_; }
|
||||
|
||||
void set_annotated(const std::map<std::string, bool>& annotated);
|
||||
|
||||
bool is_annotated(const std::string& name) const {
|
||||
return annotated_.count(name) == 1 && annotated_.at(name) == true;
|
||||
}
|
||||
|
||||
void mark_annotated(const std::string& name);
|
||||
|
||||
void clear_annotated() { annotated_.clear(); }
|
||||
|
||||
bool verify_process_mesh(const ProcessMesh& process_mesh) const;
|
||||
|
||||
bool verify_dims_mapping(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping,
|
||||
const std::vector<int64_t>& tensor_shape) const;
|
||||
|
||||
bool verify_batch_dim(int64_t dim,
|
||||
const std::vector<int64_t>& tensor_shape) const;
|
||||
|
||||
bool verify_dynamic_dims(const std::vector<bool>& dynamic_dims,
|
||||
const std::vector<int64_t>& tensor_shape) const;
|
||||
|
||||
bool verify_annotated(const std::map<std::string, bool>& annotated) const;
|
||||
|
||||
bool verify_partial_status() const;
|
||||
|
||||
bool verify(const std::vector<int64_t>& tensor_shape) const;
|
||||
bool verify_dynamic(const std::vector<int64_t>& tensor_shape) const;
|
||||
|
||||
// TensorDistAttr from_string(const std::string& dist_str);
|
||||
std::string to_string() const;
|
||||
std::string partial_status_string() const;
|
||||
|
||||
// in partial-support-stage-I partial will always be a runtime attribute,
|
||||
// there is not need to serialize it. support the partial serialization in
|
||||
// future partial-support-stage-II.
|
||||
void from_proto(const auto_parallel::TensorDistAttrProto& proto);
|
||||
|
||||
void to_proto(auto_parallel::TensorDistAttrProto* proto) const;
|
||||
|
||||
std::string serialize_to_string();
|
||||
|
||||
void parse_from_string(const std::string& data);
|
||||
|
||||
bool empty() const;
|
||||
|
||||
std::vector<std::shared_ptr<PlacementStatus>> to_placement() const;
|
||||
|
||||
// if mesh_axis is -1, check if tensor is replicated on whole process_mesh
|
||||
// if mesh_axis is not -1, check only on specific axis.
|
||||
bool is_replicated(int64_t mesh_axis = -1) const;
|
||||
|
||||
// if mesh_axis is -1, check if tensor is shard on whole process_mesh
|
||||
// if mesh_axis is not -1, check only on specific axis
|
||||
// if tensor_axis is not -1, return true only if the shard axis equal to
|
||||
// tensor_axis.
|
||||
bool is_shard(int64_t mesh_axis = -1,
|
||||
std::optional<int64_t> dim = std::nullopt) const;
|
||||
|
||||
// if mesh_axis is -1, check if tensor is partial on whole process_mesh
|
||||
// if mesh_axis is not -1, check only on specific axis.
|
||||
bool is_partial(int64_t mesh_axis = -1) const;
|
||||
|
||||
void set_skip_check_mesh(bool skip);
|
||||
|
||||
bool skip_check_mesh() const { return skip_check_mesh_; }
|
||||
|
||||
bool is_co_shard() const;
|
||||
|
||||
private:
|
||||
// delete it after all 1d vector dims_mapping_ have been upgraded to 2d.
|
||||
class PADDLE_API DimMapProxy final {
|
||||
public:
|
||||
DimMapProxy(std::vector<std::vector<int64_t>>* dims_mapping_2d,
|
||||
const ProcessMesh& process_mesh)
|
||||
: dims_mapping_2d(dims_mapping_2d), process_mesh(process_mesh) {}
|
||||
|
||||
DimMapProxy& operator=(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping);
|
||||
DimMapProxy& operator=(const std::vector<int64_t>& dims_mapping);
|
||||
DimMapProxy& operator=(const DimMapProxy& other);
|
||||
bool operator==(const DimMapProxy& other);
|
||||
bool operator!=(const DimMapProxy& other);
|
||||
operator const std::vector<std::vector<int64_t>>&() const;
|
||||
operator const std::vector<int64_t>&() const;
|
||||
|
||||
private:
|
||||
void sync_1d_map() const;
|
||||
void sync_2d_map();
|
||||
mutable std::vector<int64_t> dims_mapping_1d;
|
||||
std::vector<std::vector<int64_t>>* dims_mapping_2d;
|
||||
const ProcessMesh& process_mesh;
|
||||
};
|
||||
|
||||
static std::vector<std::string> fields_;
|
||||
ProcessMesh process_mesh_;
|
||||
std::vector<bool> dynamic_dims_;
|
||||
std::map<std::string, bool> annotated_;
|
||||
int64_t batch_dim_{0};
|
||||
int64_t chunk_id_{0};
|
||||
// partial map would be small (less than mesh.size)
|
||||
// iterate operation (copy and comparison) would more frequency than random
|
||||
// element access. <key: dim on mesh, value: reduce type>
|
||||
paddle::flat_hash_map<int64_t, ReduceType> partial_status_;
|
||||
auto_parallel::SplitFactor split_factor_map_;
|
||||
// The flag indicates whether to skip checking the process mesh.
|
||||
bool skip_check_mesh_ = false;
|
||||
|
||||
std::vector<std::vector<int64_t>> dims_mapping_;
|
||||
// for short time, backward compatible for existing spmd relus.
|
||||
DimMapProxy dims_mapping_proxy{&dims_mapping_, process_mesh_};
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const TensorDistAttr& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
PADDLE_API bool operator==(const TensorDistAttr& lhs,
|
||||
const TensorDistAttr& rhs);
|
||||
|
||||
inline bool operator!=(const TensorDistAttr& lhs, const TensorDistAttr& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,144 @@
|
||||
/* 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. */
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_mapper.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/proto_helper.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
|
||||
namespace phi::distributed::auto_parallel {
|
||||
|
||||
void DistributedMapper::set_process_id_to_device_ids(
|
||||
const std::map<int64_t, std::pair<std::string, std::vector<int64_t>>>&
|
||||
process_id_to_device_ids) {
|
||||
std::vector<std::string> device_mesh_names;
|
||||
device_mesh_names.reserve(device_meshes_.size());
|
||||
for (const auto& item : device_meshes_) {
|
||||
device_mesh_names.push_back(item.first);
|
||||
}
|
||||
for (const auto& item : process_id_to_device_ids) {
|
||||
PADDLE_ENFORCE_GE(
|
||||
item.first,
|
||||
0,
|
||||
errors::InvalidArgument(
|
||||
"The process id %d must be greater than or equal to 0.",
|
||||
item.first));
|
||||
std::string device_mesh_name = item.second.first;
|
||||
const std::vector<int64_t>& device_ids = item.second.second;
|
||||
PADDLE_ENFORCE_EQ(
|
||||
device_meshes_.count(device_mesh_name),
|
||||
1,
|
||||
errors::InvalidArgument(
|
||||
"Cannot find the device mesh %d in device_mesh ids [%s].",
|
||||
device_mesh_name,
|
||||
str_join(device_mesh_names)));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
has_duplicates(device_ids),
|
||||
false,
|
||||
errors::InvalidArgument(
|
||||
"The mapped device ids [%s] of process_mesh %d must be unique.",
|
||||
str_join(device_ids),
|
||||
item.first));
|
||||
const DeviceMesh& device_mesh = device_meshes_[device_mesh_name];
|
||||
const std::vector<int64_t> cur_device_ids = device_mesh.device_ids();
|
||||
for (int64_t device_id : device_ids) {
|
||||
bool found =
|
||||
std::find(cur_device_ids.begin(), cur_device_ids.end(), device_id) !=
|
||||
cur_device_ids.end();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
found,
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"The device id %d cannot be find in the device mesh [%s].",
|
||||
device_id,
|
||||
str_join(cur_device_ids)));
|
||||
}
|
||||
}
|
||||
process_id_to_device_ids_ = process_id_to_device_ids;
|
||||
}
|
||||
|
||||
DistributedMapper DistributedMapper::from_proto(
|
||||
const DistributedMapperProto& proto) {
|
||||
DistributedMapper dist_mapper;
|
||||
for (int i = 0; i < proto.device_meshes_size(); ++i) {
|
||||
dist_mapper.device_meshes_[proto.device_meshes(i).name()] =
|
||||
DeviceMesh::from_proto(proto.device_meshes(i));
|
||||
}
|
||||
for (int i = 0; i < proto.process_id_to_device_ids_size(); ++i) {
|
||||
int64_t process_id = proto.process_id_to_device_ids(i).process_id();
|
||||
std::string device_mesh_name =
|
||||
proto.process_id_to_device_ids(i).device_mesh_name();
|
||||
std::vector<int64_t> device_ids;
|
||||
int num_devices = proto.process_id_to_device_ids(i).device_ids_size();
|
||||
device_ids.reserve(num_devices);
|
||||
for (int j = 0; j < num_devices; ++j) {
|
||||
device_ids.push_back(proto.process_id_to_device_ids(i).device_ids(j));
|
||||
}
|
||||
dist_mapper.process_id_to_device_ids_[process_id].first = device_mesh_name;
|
||||
dist_mapper.process_id_to_device_ids_[process_id].second = device_ids;
|
||||
}
|
||||
return dist_mapper;
|
||||
}
|
||||
|
||||
void DistributedMapper::to_proto(DistributedMapperProto* proto) const {
|
||||
for (const auto& item : device_meshes_) {
|
||||
proto->mutable_device_meshes()->Add()->CopyFrom(
|
||||
distributed::to_proto(item.second));
|
||||
}
|
||||
for (const auto& outer : process_id_to_device_ids_) {
|
||||
auto proto_item = proto->mutable_process_id_to_device_ids()->Add();
|
||||
proto_item->set_process_id(outer.first);
|
||||
proto_item->set_device_mesh_name(outer.second.first);
|
||||
for (const auto& inner : outer.second.second) {
|
||||
proto_item->add_device_ids(inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string DistributedMapper::to_string() const {
|
||||
std::string mapper_str = "{device_meshes: [";
|
||||
for (const auto& item : device_meshes_) {
|
||||
mapper_str += item.second.to_string() + ", ";
|
||||
}
|
||||
mapper_str.replace(mapper_str.size() - 2, 2, "]");
|
||||
|
||||
mapper_str += "\nprocess_id_to_device_ids: [";
|
||||
for (const auto& item : process_id_to_device_ids_) {
|
||||
mapper_str += "{";
|
||||
mapper_str +=
|
||||
"process_id: " + std::to_string(item.first) + ", device_ids: [";
|
||||
for (const auto& device_id : item.second.second) {
|
||||
mapper_str +=
|
||||
"{" + item.second.first + ", " + std::to_string(device_id) + "}, ";
|
||||
}
|
||||
mapper_str.replace(mapper_str.size() - 2, 2, "]");
|
||||
mapper_str += "}, ";
|
||||
}
|
||||
mapper_str.replace(mapper_str.size() - 2, 2, "]");
|
||||
mapper_str += "}";
|
||||
return mapper_str;
|
||||
}
|
||||
|
||||
bool operator==(const DistributedMapper& lhs, const DistributedMapper& rhs) {
|
||||
if (lhs.device_meshes() != rhs.device_meshes()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.process_id_to_device_ids() != rhs.process_id_to_device_ids()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed::auto_parallel
|
||||
@@ -0,0 +1,75 @@
|
||||
/* 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. */
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/device_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
namespace auto_parallel {
|
||||
|
||||
class DistributedMapperProto;
|
||||
|
||||
class PADDLE_API DistributedMapper {
|
||||
public:
|
||||
DistributedMapper() = default;
|
||||
|
||||
const std::map<std::string, DeviceMesh>& device_meshes() const {
|
||||
return device_meshes_;
|
||||
}
|
||||
|
||||
const DeviceMesh& device_mesh(const std::string& name) const {
|
||||
return device_meshes_.at(name);
|
||||
}
|
||||
|
||||
void add_device_mesh(const DeviceMesh& device_mesh) {
|
||||
device_meshes_[device_mesh.name()] = device_mesh;
|
||||
}
|
||||
|
||||
const std::map<int64_t, std::pair<std::string, std::vector<int64_t>>>&
|
||||
process_id_to_device_ids() const {
|
||||
return process_id_to_device_ids_;
|
||||
}
|
||||
|
||||
void set_process_id_to_device_ids(
|
||||
const std::map<int64_t, std::pair<std::string, std::vector<int64_t>>>&
|
||||
process_id_to_device_ids);
|
||||
|
||||
// DistributedMapper from_string(const std::string& mapper_str);
|
||||
std::string to_string() const;
|
||||
|
||||
static DistributedMapper from_proto(const DistributedMapperProto& proto);
|
||||
void to_proto(DistributedMapperProto* proto) const;
|
||||
|
||||
private:
|
||||
std::map<std::string, DeviceMesh> device_meshes_;
|
||||
std::map<int64_t, std::pair<std::string, std::vector<int64_t>>>
|
||||
process_id_to_device_ids_;
|
||||
};
|
||||
|
||||
PADDLE_API bool operator==(const DistributedMapper& lhs,
|
||||
const DistributedMapper& rhs);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os,
|
||||
const DistributedMapper& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace auto_parallel
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,53 @@
|
||||
/* 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/core/distributed/auto_parallel/dist_meta_tensor.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
DDim DistMetaTensor::dims() const {
|
||||
// member values in tensor_ have higher priority than those in DistMetaTensor
|
||||
if (tensor_ != nullptr) {
|
||||
PADDLE_ENFORCE_EQ(this->is_dist(),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"The current MetaTensor doesn't contains "
|
||||
"DistTensor when call `dims` method."));
|
||||
return MetaTensor::dims();
|
||||
} else {
|
||||
return dims_;
|
||||
}
|
||||
}
|
||||
|
||||
const distributed::TensorDistAttr& DistMetaTensor::dist_attr() const {
|
||||
// member values in tensor_ have higher priority than those in DistMetaTensor
|
||||
if (tensor_ != nullptr) {
|
||||
PADDLE_ENFORCE_EQ(this->is_dist(),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"The current MetaTensor doesn't contains "
|
||||
"DistTensor when call `dist_attr` method."));
|
||||
return static_cast<phi::distributed::DistTensor*>(tensor_)->dist_attr();
|
||||
} else {
|
||||
return dist_attr_;
|
||||
}
|
||||
}
|
||||
|
||||
bool DistMetaTensor::initialized() const {
|
||||
return tensor_ != nullptr || dist_attr_ != TensorDistAttr();
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,72 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/meta_tensor.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class DistMetaTensor : public MetaTensor {
|
||||
public:
|
||||
DistMetaTensor() : MetaTensor() {}
|
||||
|
||||
// supporting implicit construction is easier to use
|
||||
DistMetaTensor(TensorBase* tensor) // NOLINT
|
||||
: MetaTensor(tensor) {}
|
||||
DistMetaTensor(const TensorBase& tensor) // NOLINT
|
||||
: MetaTensor(tensor) {}
|
||||
DistMetaTensor(const TensorBase* tensor) // NOLINT
|
||||
: MetaTensor(tensor) {}
|
||||
DistMetaTensor(TensorBase& tensor) // NOLINT
|
||||
: MetaTensor(tensor) {}
|
||||
// For static mode only
|
||||
DistMetaTensor(const DDim& dims, const TensorDistAttr& dist_attr)
|
||||
: dims_(dims), dist_attr_(dist_attr) {}
|
||||
|
||||
DistMetaTensor(DistMetaTensor&&) = default;
|
||||
DistMetaTensor& operator=(DistMetaTensor&&) = default;
|
||||
DistMetaTensor(const DistMetaTensor&) = default;
|
||||
DistMetaTensor& operator=(const DistMetaTensor&) = default;
|
||||
|
||||
virtual ~DistMetaTensor() = default;
|
||||
|
||||
PADDLE_API DDim dims() const override;
|
||||
|
||||
const distributed::TensorDistAttr& dist_attr() const;
|
||||
|
||||
PADDLE_API bool initialized() const override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Note: When using the semi-automatic parallel segmentation derivation rules
|
||||
* of the static graph, in order to facilitate the packaging of the input
|
||||
* parameters of the construction, the DistMetaTensor is inherited and
|
||||
* encapsulated, and the class members dims_ and dist_attr_ are added to it.
|
||||
*
|
||||
* The information contained in these two members is also in the tensor of the
|
||||
* meta_tensor of the base class, and there is redundancy.
|
||||
*
|
||||
* We need to pay attention when using it to ensure the consistency.
|
||||
* These two members are read-only, and their values cannot be changed
|
||||
* after construction. To change their values, they need to be set
|
||||
* directly in tensor_*/
|
||||
DDim dims_;
|
||||
TensorDistAttr dist_attr_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,340 @@
|
||||
// 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/core/distributed/auto_parallel/dist_tensor.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/api/lib/data_transform.h"
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function_registry.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
inline void check_defined(const DistTensor& dist_tensor,
|
||||
std::string method_hint) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
dist_tensor.defined(),
|
||||
true,
|
||||
common::errors::Unimplemented(
|
||||
"DistTensor is not defined yet when `%s` method is called.",
|
||||
method_hint));
|
||||
}
|
||||
|
||||
TensorDistAttr ToTensorDistAttr(const ProcessMesh& process_mesh,
|
||||
const Placements& placements,
|
||||
const DDim& dims) {
|
||||
TensorDistAttr dist_attr(vectorize(dims));
|
||||
// Step1: set process_mesh
|
||||
dist_attr.set_process_mesh(process_mesh);
|
||||
|
||||
// Step2: set dim_mapping
|
||||
std::vector<std::vector<int64_t>> dim_mapping(dims.size());
|
||||
for (size_t i = 0; i < placements.size(); i++) {
|
||||
const auto& cur_placement = placements[i];
|
||||
if (!cur_placement->is_shard()) {
|
||||
continue;
|
||||
}
|
||||
const auto& shard = dynamic_cast<const Shard&>(*cur_placement);
|
||||
dim_mapping[shard.get_dim()].push_back(i);
|
||||
dist_attr.set_split_factor(i, shard.get_split_factor());
|
||||
}
|
||||
|
||||
auto compare_functor = [&](size_t a, size_t b) {
|
||||
const auto& shard_a = dynamic_cast<const Shard&>(*placements[a]);
|
||||
const auto& shard_b = dynamic_cast<const Shard&>(*placements[b]);
|
||||
return shard_a.get_co_shard_order() < shard_b.get_co_shard_order();
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < dim_mapping.size(); i++) {
|
||||
auto& mesh_dims = dim_mapping[i];
|
||||
if (mesh_dims.size() > 1) {
|
||||
std::sort(mesh_dims.begin(), mesh_dims.end(), compare_functor);
|
||||
}
|
||||
}
|
||||
dist_attr.set_dims_mapping(dim_mapping);
|
||||
|
||||
// Step3: set partial_status
|
||||
paddle::flat_hash_map<int64_t, ReduceType> partial_status;
|
||||
for (size_t i = 0; i < placements.size(); ++i) {
|
||||
auto& p = placements[i];
|
||||
if (p->is_partial()) {
|
||||
partial_status.insert({i, dynamic_cast<Partial&>(*p).get_reduce_type()});
|
||||
}
|
||||
}
|
||||
dist_attr.set_partial_status(partial_status);
|
||||
|
||||
// Step4: mark annotated
|
||||
dist_attr.mark_annotated("process_mesh");
|
||||
dist_attr.mark_annotated("dims_mapping");
|
||||
return dist_attr;
|
||||
}
|
||||
|
||||
Placements ToPlacements(const TensorDistAttr& dist_attr) {
|
||||
auto& process_mesh = dist_attr.process_mesh();
|
||||
Placements placements;
|
||||
placements.resize(process_mesh.ndim(), std::make_shared<Replicate>());
|
||||
|
||||
auto& partial_status = dist_attr.partial_status();
|
||||
for (const auto& pair : partial_status) {
|
||||
placements[pair.first] = std::make_shared<Partial>(pair.second);
|
||||
}
|
||||
|
||||
const std::vector<std::vector<int64_t>>& dim_mapping =
|
||||
dist_attr.multi_dims_mapping();
|
||||
for (size_t t_dim = 0; t_dim < dim_mapping.size(); t_dim++) {
|
||||
auto m_dims = dim_mapping[t_dim];
|
||||
|
||||
bool is_co_shard = m_dims.size() > 1;
|
||||
|
||||
for (size_t idx = 0; idx < m_dims.size(); idx++) {
|
||||
int64_t mesh_dim = m_dims[idx];
|
||||
int64_t split_factor = dist_attr.get_split_factor(mesh_dim);
|
||||
auto& p = placements.at(mesh_dim);
|
||||
|
||||
if (p->is_shard()) {
|
||||
PADDLE_THROW(common::errors::PreconditionNotMet(
|
||||
"ProcessMesh dimension can't be mapped to two dimension of the "
|
||||
"same tensor: {%d} and {%d}",
|
||||
t_dim,
|
||||
dynamic_cast<Shard&>(*p).get_dim()));
|
||||
} else if (p->is_partial()) {
|
||||
PADDLE_THROW(common::errors::PreconditionNotMet(
|
||||
"ProcessMesh dimension {%d} cannot be both shard and partial!",
|
||||
mesh_dim));
|
||||
}
|
||||
// TODO(lfw): add mesh_dim >= 0 check
|
||||
placements[mesh_dim] = is_co_shard
|
||||
? std::make_shared<CoShard>(t_dim, idx)
|
||||
: std::make_shared<Shard>(t_dim, split_factor);
|
||||
}
|
||||
}
|
||||
return placements;
|
||||
}
|
||||
|
||||
DistTensor::DistTensor() : value_(std::make_shared<DenseTensor>()) {}
|
||||
|
||||
DistTensor::DistTensor(DataType dtype)
|
||||
: value_(std::make_shared<DenseTensor>(dtype)) {}
|
||||
|
||||
DistTensor::DistTensor(const std::shared_ptr<DenseTensor>& global_value,
|
||||
const TensorDistAttr& dist_attr)
|
||||
: global_dims_(global_value->dims()), dist_attr_(dist_attr) {
|
||||
process_mesh_ = dist_attr_.process_mesh();
|
||||
placements_ = ToPlacements(dist_attr);
|
||||
|
||||
// If the current rank doesn't in process_mesh, we should create an
|
||||
// uninitialized tensor only with tensor_meta.
|
||||
if (IsCurRankInMesh(dist_attr.process_mesh())) {
|
||||
if (!dist_attr.is_replicated()) {
|
||||
value_ = std::make_shared<DenseTensor>();
|
||||
// 1. create replicated global tensor
|
||||
TensorDistAttr replicated_dist_attr(
|
||||
common::vectorize(global_value->dims()));
|
||||
replicated_dist_attr.set_process_mesh(dist_attr.process_mesh());
|
||||
DistTensor replicated_tensor(global_value, replicated_dist_attr);
|
||||
|
||||
// 2. reshard from replicated to other state
|
||||
VLOG(4) << "Reshard tensor: "
|
||||
<< paddle::experimental::ReshardDebugInfo(replicated_tensor,
|
||||
dist_attr);
|
||||
auto* func = ChooseProperReshardFunction(replicated_tensor, dist_attr);
|
||||
auto* dev_ctx = DeviceContextPool::Instance().Get(global_value->place());
|
||||
func->Eval(dev_ctx, replicated_tensor, dist_attr, this);
|
||||
} else {
|
||||
value_ = global_value;
|
||||
}
|
||||
} else {
|
||||
value_ = std::make_shared<DenseTensor>(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, global_value->place()),
|
||||
DenseTensorMeta(global_value->meta()));
|
||||
}
|
||||
}
|
||||
|
||||
DistTensor::DistTensor(const std::shared_ptr<DenseTensor>& local_value,
|
||||
const DDim& global_dims,
|
||||
const ProcessMesh& process_mesh,
|
||||
const Placements& placements)
|
||||
: global_dims_(global_dims),
|
||||
process_mesh_(process_mesh),
|
||||
placements_(placements) {
|
||||
dist_attr_ = ToTensorDistAttr(process_mesh_, placements_, global_dims_);
|
||||
if (IsCurRankInMesh(process_mesh)) {
|
||||
value_ = local_value;
|
||||
} else {
|
||||
value_ = std::make_shared<DenseTensor>(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, local_value->place()),
|
||||
DenseTensorMeta(local_value->dtype(), make_ddim({0})));
|
||||
}
|
||||
}
|
||||
|
||||
DistTensor::DistTensor(const std::shared_ptr<DenseTensor>& local_value,
|
||||
const DDim& global_dims,
|
||||
const TensorDistAttr& dist_attr)
|
||||
: global_dims_(global_dims), dist_attr_(dist_attr) {
|
||||
process_mesh_ = dist_attr_.process_mesh();
|
||||
placements_ = ToPlacements(dist_attr);
|
||||
if (IsCurRankInMesh(process_mesh_)) {
|
||||
value_ = local_value;
|
||||
} else {
|
||||
value_ = std::make_shared<DenseTensor>(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, local_value->place()),
|
||||
DenseTensorMeta(local_value->dtype(), global_dims_));
|
||||
}
|
||||
}
|
||||
|
||||
DistTensor::DistTensor(const std::shared_ptr<DenseTensor>& global_value,
|
||||
const ProcessMesh& process_mesh,
|
||||
const Placements& placements)
|
||||
: global_dims_(global_value->dims()) {
|
||||
process_mesh_ = process_mesh;
|
||||
placements_ = placements;
|
||||
// If the dims.size() == -1, the dims=[0] by default, which is not consistent
|
||||
// and will cause ToTensorDistAttr's error.
|
||||
if (global_dims_ == DDim()) {
|
||||
global_dims_ = make_ddim({});
|
||||
}
|
||||
dist_attr_ = ToTensorDistAttr(process_mesh_, placements_, global_dims_);
|
||||
|
||||
// If the current rank doesn't in process_mesh, we should create an
|
||||
// uninitialized tensor only with dist_tensor_meta_.
|
||||
if (IsCurRankInMesh(process_mesh)) {
|
||||
if (!dist_attr_.is_replicated()) {
|
||||
if (global_value->initialized()) {
|
||||
value_ = std::make_shared<DenseTensor>();
|
||||
// 1. create replicated global tensor
|
||||
TensorDistAttr replicated_dist_attr(
|
||||
common::vectorize(global_value->dims()));
|
||||
replicated_dist_attr.set_process_mesh(process_mesh);
|
||||
DistTensor replicated_tensor(global_value, replicated_dist_attr);
|
||||
|
||||
// 2. reshard from replicated to other state
|
||||
VLOG(4) << "Reshard tensor: "
|
||||
<< paddle::experimental::ReshardDebugInfo(replicated_tensor,
|
||||
dist_attr_);
|
||||
auto* func = ChooseProperReshardFunction(replicated_tensor, dist_attr_);
|
||||
auto* dev_ctx =
|
||||
DeviceContextPool::Instance().Get(global_value->place());
|
||||
func->Eval(dev_ctx, replicated_tensor, dist_attr_, this);
|
||||
} else {
|
||||
// For lazy init, the global value is an uninitialized tensor.
|
||||
// Just infer the local shape of the dist tensor.
|
||||
value_ = global_value;
|
||||
value_->Resize(
|
||||
InferShapeForReshardFromReplicate(global_value, dist_attr_));
|
||||
}
|
||||
} else {
|
||||
value_ = global_value;
|
||||
}
|
||||
} else {
|
||||
value_ = std::make_shared<DenseTensor>(
|
||||
std::make_shared<phi::Allocation>(nullptr, 0, global_value->place()),
|
||||
DenseTensorMeta(global_value->meta()));
|
||||
}
|
||||
}
|
||||
|
||||
DistTensor::DistTensor(const DDim& dims, const TensorDistAttr& dist_attr)
|
||||
: global_dims_(dims),
|
||||
dist_attr_(dist_attr),
|
||||
value_(std::make_shared<DenseTensor>()) {
|
||||
process_mesh_ = dist_attr.process_mesh();
|
||||
placements_ = ToPlacements(dist_attr);
|
||||
}
|
||||
|
||||
void DistTensor::unsafe_set_dims(const DDim& dims) {
|
||||
if (this->initialized()) {
|
||||
VLOG(6) << "You try to set an initialized DistTensor's global dims. "
|
||||
"Make sure you are aware of where you change its dims.";
|
||||
}
|
||||
global_dims_ = dims;
|
||||
}
|
||||
|
||||
void DistTensor::unsafe_set_dist_attr(const TensorDistAttr& dist_attr) {
|
||||
if (this->initialized()) {
|
||||
VLOG(6) << "You try to set an initialized DistTensor's dist attr. "
|
||||
"Make sure you are aware of where you change its dist attr.";
|
||||
}
|
||||
dist_attr_ = dist_attr;
|
||||
process_mesh_ = dist_attr.process_mesh();
|
||||
placements_ = ToPlacements(dist_attr);
|
||||
}
|
||||
|
||||
int64_t DistTensor::numel() const {
|
||||
// DistTensor with uninitialized local tensor can
|
||||
// also have numel.
|
||||
return product(global_dims_);
|
||||
}
|
||||
|
||||
const DDim& DistTensor::local_dims() const {
|
||||
check_defined(*this, "local_dims");
|
||||
return value_->dims();
|
||||
}
|
||||
|
||||
bool DistTensor::valid() const {
|
||||
check_defined(*this, "valid");
|
||||
return value_->valid();
|
||||
}
|
||||
|
||||
bool DistTensor::defined() const { return value_->holder_ != nullptr; }
|
||||
|
||||
bool DistTensor::has_allocation() const {
|
||||
return value_->holder_ != nullptr && (value_->holder_->ptr() || numel() != 0);
|
||||
}
|
||||
|
||||
bool DistTensor::initialized() const {
|
||||
return value_->holder_ != nullptr && value_->holder_->ptr();
|
||||
}
|
||||
|
||||
DataType DistTensor::dtype() const {
|
||||
// DistTensor with uninitialized local tensor can
|
||||
// also have dtype.
|
||||
return value_->dtype();
|
||||
}
|
||||
|
||||
DataLayout DistTensor::layout() const {
|
||||
// DistTensor with uninitialized local tensor can
|
||||
// also have layout.
|
||||
return value_->layout();
|
||||
}
|
||||
|
||||
const Place& DistTensor::place() const {
|
||||
check_defined(*this, "place");
|
||||
return value_->holder_->place();
|
||||
}
|
||||
|
||||
void* DistTensor::AllocateFrom(Allocator* allocator,
|
||||
DataType dtype,
|
||||
size_t requested_size,
|
||||
bool fake_alloc) {
|
||||
PADDLE_THROW(common::errors::Unavailable(
|
||||
"The DistTensor Cannot allocate memory directly and needs to perform "
|
||||
"memory operations through its DenseTensor value."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DistTensor::unsafe_set_skip_check_mesh(bool skip) {
|
||||
VLOG(6) << "You try to set an initialized DistTensor's dist attr. "
|
||||
"Make sure you are aware of where you change its dist attr.";
|
||||
dist_attr_.set_skip_check_mesh(skip);
|
||||
}
|
||||
|
||||
void DistTensor::clear() {
|
||||
if (value_) {
|
||||
value_->clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,216 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "paddle/phi/common/reduce_type.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/placement_types.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
class ReshardFunction;
|
||||
class Shard;
|
||||
class Partial;
|
||||
class Replicate;
|
||||
|
||||
PADDLE_API TensorDistAttr ToTensorDistAttr(const ProcessMesh& process_mesh,
|
||||
const Placements& placements,
|
||||
const DDim& dims);
|
||||
|
||||
PADDLE_API Placements ToPlacements(const TensorDistAttr& dist_attr);
|
||||
|
||||
class PADDLE_API DistTensor final
|
||||
: public phi::TensorBase,
|
||||
public phi::TypeInfoTraits<phi::TensorBase, DistTensor> {
|
||||
public:
|
||||
/// \brief Careful to create dist tensor using default constructor.
|
||||
/// this should only used in reshard for now, and the dist properties
|
||||
/// will be set by reshard later.
|
||||
DistTensor();
|
||||
|
||||
/// \brief Construct a dist tensor based dtype.
|
||||
/// \param dtype The dtype of the current tensor.
|
||||
explicit DistTensor(DataType dtype);
|
||||
|
||||
/// \brief Construct a dist tensor based dense tensor.
|
||||
/// \param global_value The global dense tensor of the current tensor.
|
||||
/// \param dist_attr The distributed attributes of the current tensor.
|
||||
DistTensor(const std::shared_ptr<DenseTensor>& global_value,
|
||||
const TensorDistAttr& dist_attr);
|
||||
|
||||
/// \brief Construct a dist tensor based dense tensor.
|
||||
/// \param process_mesh The process mesh of the current tensor.
|
||||
/// \param placements The distributed placements of the current tensor.
|
||||
DistTensor(const std::shared_ptr<DenseTensor>& global_value,
|
||||
const ProcessMesh& process_mesh,
|
||||
const Placements& placements);
|
||||
|
||||
/// \brief Construct a dist tensor based local dense tensor.
|
||||
/// \param global_dims The global dim of the dist tensor.
|
||||
/// \param dist_attr The distributed attributes of the current tensor.
|
||||
DistTensor(const std::shared_ptr<DenseTensor>& local_value,
|
||||
const DDim& global_dims,
|
||||
const TensorDistAttr& dist_attr);
|
||||
|
||||
/// \brief Construct a dist tensor based local dense tensor.
|
||||
/// \param global_dims The global dim of the dist tensor.
|
||||
/// \param process_mesh The process mesh of the current tensor.
|
||||
/// \param placements The distributed placements of the current tensor.
|
||||
DistTensor(const std::shared_ptr<DenseTensor>& local_value,
|
||||
const DDim& global_dims,
|
||||
const ProcessMesh& process_mesh,
|
||||
const Placements& placements);
|
||||
|
||||
/// \brief Construct a empty dist tensor (for infer spmd)
|
||||
/// \param dims The global dimension of the current Tensor.
|
||||
/// \param dist_attr The distributed attributes of the current tensor.
|
||||
DistTensor(const DDim& dims, const TensorDistAttr& dist_attr);
|
||||
|
||||
/// \brief Destroy the tensor object and release exclusive resources.
|
||||
virtual ~DistTensor() = default;
|
||||
|
||||
/// \brief Returns the name of the class for type traits.
|
||||
/// \return The name of the class.
|
||||
static const char* name() { return "DistTensor"; }
|
||||
|
||||
/// \brief Returns the global dims of the dist tensor.
|
||||
/// \return The global dims of the dist tensor.
|
||||
const DDim& dims() const override { return global_dims_; }
|
||||
|
||||
/// \brief Set the global dims of the dist tensor.
|
||||
/// \return void
|
||||
void unsafe_set_dims(const DDim& dims);
|
||||
|
||||
/// \brief Returns the dist attr of current dist tensor.
|
||||
/// \return The TensorDistAttr's const reference
|
||||
const TensorDistAttr& dist_attr() const { return dist_attr_; }
|
||||
|
||||
/// \brief Returns the process_mesh of current dist tensor.
|
||||
/// \return The ProcessMesh's const reference
|
||||
const ProcessMesh& process_mesh() const { return process_mesh_; }
|
||||
|
||||
/// \brief Returns the placements of current dist tensor.
|
||||
/// \return The Placements's const reference
|
||||
const Placements& placements() const { return placements_; }
|
||||
|
||||
/// \brief Returns the num_shard of current dist tensor.
|
||||
/// \return int64_t
|
||||
int64_t num_shard() const {
|
||||
int64_t num_shard = 1;
|
||||
const auto& mesh_shape = process_mesh_.shape();
|
||||
for (size_t i = 0; i < placements_.size(); i++) {
|
||||
if (placements_[i]->is_shard()) {
|
||||
num_shard *= mesh_shape[i];
|
||||
}
|
||||
}
|
||||
return num_shard;
|
||||
}
|
||||
/// \brief Set the dist attr of current dist tensor.
|
||||
/// \return void
|
||||
void unsafe_set_dist_attr(const TensorDistAttr& dist_attr);
|
||||
|
||||
/// \brief Returns the dense tensor value's const reference in dist tensor.
|
||||
/// \return The DenseTensor value's const reference
|
||||
const DenseTensor& value() const { return *value_; }
|
||||
|
||||
/// \brief Returns the shared_ptr of dense tensor value's in dist tensor.
|
||||
/// \return The shared_ptr of dense tensor value
|
||||
std::shared_ptr<DenseTensor> shared_value() { return value_; }
|
||||
|
||||
/// \brief Returns the mutable dense tensor value in dist tensor.
|
||||
/// \note If DenseTensor value is modified externally, the corresponding
|
||||
/// relationship between it and the current tensor's global dims and
|
||||
/// dist attr may be destroyed, which may introduce some subtle bugs,
|
||||
/// so you need to make sure to consider it thoroughly when using
|
||||
/// this method.
|
||||
/// \return The mutable pointer of DenseTensor value
|
||||
DenseTensor* unsafe_mutable_value() { return value_.get(); }
|
||||
|
||||
/// \brief Returns the global dims of the dist tensor.
|
||||
/// \return The global dims of the dist tensor.
|
||||
const DDim& local_dims() const;
|
||||
|
||||
/// \brief Test whether the holder is created.
|
||||
/// \return Whether the holder is created.
|
||||
bool has_allocation() const override;
|
||||
|
||||
/// \brief Returns the global number of elements contained in tensor.
|
||||
/// \return The number of elements contained in tensor.
|
||||
int64_t numel() const override;
|
||||
|
||||
/// \brief Test whether the dense tensor value's storage is allocated.
|
||||
/// \return Whether the dense tensor value's storage is allocated.
|
||||
bool initialized() const override;
|
||||
|
||||
/// \brief Test whether the dense tensor value is defined.
|
||||
/// \return Whether the dense tensor value is defined.
|
||||
bool defined() const;
|
||||
|
||||
/// \brief Test whether the metadata is valid.
|
||||
/// \return Whether the metadata is valid.
|
||||
bool valid() const override;
|
||||
|
||||
/// \brief Returns the data type of the tensor.
|
||||
/// \return The data type of the tensor.
|
||||
DataType dtype() const override;
|
||||
|
||||
/// \brief Returns the data layout of the tensor.
|
||||
/// \return The data layout of the tensor.
|
||||
DataLayout layout() const override;
|
||||
|
||||
/// \brief Returns the data place of the tensor.
|
||||
/// \return The data place of the tensor.
|
||||
const Place& place() const override;
|
||||
|
||||
/// \brief Allocate memory with requested size from allocator.
|
||||
/// \return The mutable data pointer value of type T.
|
||||
void* AllocateFrom(Allocator* allocator,
|
||||
DataType dtype,
|
||||
size_t requested_size = 0,
|
||||
bool fake_alloc = false) override;
|
||||
|
||||
/// \brief Set the flag indicating whether to skip checking the process mesh.
|
||||
/// \note Currently only used for the MoE apis,
|
||||
/// it receives the inputs with different process meshes and outputs the dist
|
||||
/// tensor with global process mesh.
|
||||
/// \return void
|
||||
void unsafe_set_skip_check_mesh(bool skip);
|
||||
|
||||
bool skip_check_mesh() const { return dist_attr_.skip_check_mesh(); }
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
friend class ReshardFunction;
|
||||
|
||||
// The global dimensions(shape), will move to DistTensorMeta
|
||||
DDim global_dims_;
|
||||
// The distributed attributes, will remove in the future
|
||||
TensorDistAttr dist_attr_;
|
||||
// The local DenseTensor value
|
||||
std::shared_ptr<DenseTensor> value_;
|
||||
|
||||
ProcessMesh process_mesh_;
|
||||
|
||||
Placements placements_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,206 @@
|
||||
/* 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/core/distributed/auto_parallel/inferspmd_utils.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
InferSpmdContext::InferSpmdContext(
|
||||
paddle::small_vector<DistMetaTensor, kInputSmallVectorSize> inputs,
|
||||
paddle::small_vector<Attribute, kAttrSmallVectorSize> attrs) {
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
EmplaceBackInput(inputs[i]);
|
||||
}
|
||||
for (size_t i = 0; i < attrs.size(); i++) {
|
||||
EmplaceBackAttr(attrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void InferSpmdContext::EmplaceBackInput(DistMetaTensor input) {
|
||||
int index = static_cast<int>(inputs_.size());
|
||||
inputs_.emplace_back(std::move(input));
|
||||
input_range_.emplace_back(std::pair<int, int>(index, index + 1));
|
||||
}
|
||||
|
||||
void InferSpmdContext::EmplaceBackInputs(
|
||||
paddle::small_vector<DistMetaTensor, kInputSmallVectorSize> inputs) {
|
||||
int index = static_cast<int>(inputs_.size());
|
||||
input_range_.emplace_back(std::pair<int, int>(index, index + inputs.size()));
|
||||
inputs_.insert(inputs_.end(),
|
||||
std::make_move_iterator(inputs.begin()),
|
||||
std::make_move_iterator(inputs.end()));
|
||||
}
|
||||
|
||||
void InferSpmdContext::EmplaceBackAttr(Attribute attr) {
|
||||
attrs_.emplace_back(std::move(attr));
|
||||
}
|
||||
|
||||
const DistMetaTensor& InferSpmdContext::InputAt(size_t idx) const {
|
||||
return inputs_.at(idx);
|
||||
}
|
||||
|
||||
template <typename AttrType>
|
||||
AttrType InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
return paddle::get<AttrType>(attrs_.at(idx));
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `%s`.",
|
||||
attrs_.at(idx).type().name(),
|
||||
std::type_index(typeid(AttrType)).name()));
|
||||
}
|
||||
}
|
||||
|
||||
template float InferSpmdContext::AttrAt(size_t idx) const;
|
||||
template int InferSpmdContext::AttrAt(size_t idx) const;
|
||||
template int64_t InferSpmdContext::AttrAt(size_t idx) const;
|
||||
template DataType InferSpmdContext::AttrAt(size_t idx) const;
|
||||
|
||||
template <>
|
||||
bool InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
auto attr = attrs_.at(idx);
|
||||
if (attr.type() == typeid(int)) {
|
||||
return static_cast<bool>(paddle::get<int>(attr));
|
||||
} else {
|
||||
return paddle::get<bool>(attr);
|
||||
}
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `bool`.",
|
||||
attrs_.at(idx).type().name()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
double InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
auto attr = attrs_.at(idx);
|
||||
if (attr.type() == typeid(float)) {
|
||||
return static_cast<double>(paddle::get<float>(attr));
|
||||
} else {
|
||||
return paddle::get<double>(attr);
|
||||
}
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `double`.",
|
||||
attrs_.at(idx).type().name()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<int> InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
auto attr = attrs_.at(idx);
|
||||
if (attr.type() == typeid(std::vector<bool>)) {
|
||||
std::vector<bool> val = PADDLE_GET_CONST(std::vector<bool>, attr);
|
||||
return std::vector<int>(val.begin(), val.end());
|
||||
} else if (attr.type() == typeid(std::vector<int64_t>) &&
|
||||
paddle::get<std::vector<int64_t>>(attr).empty()) {
|
||||
return std::vector<int>();
|
||||
} else {
|
||||
return paddle::get<std::vector<int>>(attr);
|
||||
}
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `std::vector<int>`.",
|
||||
attrs_.at(idx).type().name()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
std::vector<int64_t> InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
auto attr = attrs_.at(idx);
|
||||
if (attr.type() == typeid(std::vector<bool>)) {
|
||||
std::vector<bool> val = PADDLE_GET_CONST(std::vector<bool>, attr);
|
||||
return std::vector<int64_t>(val.begin(), val.end());
|
||||
} else if (attr.type() == typeid(std::vector<int>)) {
|
||||
std::vector<int> val = PADDLE_GET_CONST(std::vector<int>, attr);
|
||||
return std::vector<int64_t>(val.begin(), val.end());
|
||||
} else {
|
||||
return PADDLE_GET_CONST(std::vector<int64_t>, attr);
|
||||
}
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `std::vector<int64_t>`.",
|
||||
attrs_.at(idx).type().name()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string InferSpmdContext::AttrAt(size_t idx) const {
|
||||
try {
|
||||
auto attr = attrs_.at(idx);
|
||||
return PADDLE_GET_CONST(std::string, attr);
|
||||
} catch (paddle::bad_variant_access const& e) {
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"Attribute cast error in InferSpmd Context, the input attr type is "
|
||||
"`%s`, but the expected attribute type is `std::string`.",
|
||||
attrs_.at(idx).type().name()));
|
||||
}
|
||||
}
|
||||
|
||||
const Attribute& InferSpmdContext::AttrAt(size_t idx) const {
|
||||
return attrs_.at(idx);
|
||||
}
|
||||
|
||||
const std::pair<int, int>& InferSpmdContext::InputRangeAt(size_t idx) const {
|
||||
return input_range_.at(idx);
|
||||
}
|
||||
|
||||
const std::vector<const DistMetaTensor*> InferSpmdContext::InputsBetween(
|
||||
size_t start, size_t end) const {
|
||||
std::vector<const DistMetaTensor*> result;
|
||||
result.reserve(end - start);
|
||||
for (size_t i = start; i < end; ++i) {
|
||||
auto& in = inputs_.at(i);
|
||||
result.emplace_back(&in);
|
||||
// result.emplace_back(in.initialized() ? &in : nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SpmdRuleFactory& SpmdRuleFactory::Instance() {
|
||||
static SpmdRuleFactory g_spmd_rule_map;
|
||||
return g_spmd_rule_map;
|
||||
}
|
||||
|
||||
bool SpmdRuleFactory::ContainsSpmdRule(const std::string& kernel_name) const {
|
||||
return spmd_rule_map_.count(kernel_name) > 0;
|
||||
}
|
||||
|
||||
int SpmdRuleFactory::InsertSpmdRule(std::string kernel_name, SpmdRule rule) {
|
||||
spmd_rule_map_.insert({std::move(kernel_name), rule});
|
||||
return 0;
|
||||
}
|
||||
|
||||
const SpmdRule& SpmdRuleFactory::GetSpmdRule(
|
||||
const std::string& kernel_name) const {
|
||||
auto it = spmd_rule_map_.find(kernel_name);
|
||||
PADDLE_ENFORCE_NE(
|
||||
it,
|
||||
spmd_rule_map_.end(),
|
||||
common::errors::NotFound("`%s` Kernel's Spmd rules is not registered.",
|
||||
kernel_name));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,256 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/phi/common/scalar.h"
|
||||
#include "paddle/phi/core/attribute.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_meta_tensor.h"
|
||||
#include "paddle/phi/core/distributed/type_defs.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/type_defs.h"
|
||||
#include "paddle/utils/any.h"
|
||||
#include "paddle/utils/flat_hash_map.h"
|
||||
#include "paddle/utils/small_vector.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API InferSpmdContext {
|
||||
public:
|
||||
InferSpmdContext() = default;
|
||||
InferSpmdContext(
|
||||
paddle::small_vector<DistMetaTensor, kInputSmallVectorSize> inputs,
|
||||
paddle::small_vector<Attribute, kAttrSmallVectorSize> attrs);
|
||||
|
||||
void EmplaceBackInput(DistMetaTensor input);
|
||||
void EmplaceBackAttr(Attribute attr);
|
||||
void EmplaceBackInputs(
|
||||
paddle::small_vector<DistMetaTensor, kInputSmallVectorSize> inputs);
|
||||
|
||||
const DistMetaTensor& InputAt(size_t idx) const;
|
||||
|
||||
const std::pair<int, int>& InputRangeAt(size_t idx) const;
|
||||
const std::vector<const DistMetaTensor*> InputsBetween(size_t start,
|
||||
size_t end) const;
|
||||
|
||||
template <typename AttrType>
|
||||
AttrType AttrAt(size_t idx) const;
|
||||
|
||||
const Attribute& AttrAt(size_t idx) const;
|
||||
|
||||
private:
|
||||
// Now we only need `inputs`, for backward, the `output` is passed as input
|
||||
paddle::small_vector<DistMetaTensor, kInputSmallVectorSize> inputs_;
|
||||
// Because the attribute arguments of dygraph do not have `attr name`,
|
||||
// so we use vector instead of map
|
||||
paddle::small_vector<Attribute, kAttrSmallVectorSize> attrs_;
|
||||
// for vector arguments
|
||||
paddle::small_vector<std::pair<int, int>, kInputSmallVectorSize> input_range_;
|
||||
};
|
||||
|
||||
using InferSpmdFn = SpmdInfo (*)(const InferSpmdContext&);
|
||||
|
||||
#define PD_INFER_SPMD(...) \
|
||||
::phi::distributed::InferSpmdFnImpl<decltype(&__VA_ARGS__), \
|
||||
&__VA_ARGS__>::Call
|
||||
|
||||
template <typename T>
|
||||
struct InferSpmdTypeTag {};
|
||||
|
||||
template <typename Fn, Fn fn>
|
||||
struct InferSpmdFnImpl;
|
||||
|
||||
template <typename Return, typename... Args, Return (*infer_spmd_fn)(Args...)>
|
||||
struct InferSpmdFnImpl<Return (*)(Args...), infer_spmd_fn> {
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx) {
|
||||
return InferSpmdFnCallHelper<Args..., InferSpmdTypeTag<int>>::
|
||||
template Call<0, 0>(ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... RemainingArgs>
|
||||
struct InferSpmdFnCallHelper;
|
||||
|
||||
// TODO(chenweihang): support other input type later as needed
|
||||
template <typename... Tail>
|
||||
struct InferSpmdFnCallHelper<const DistMetaTensor&, Tail...> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx, PreviousArgs&... pargs) {
|
||||
static_assert(attr_idx == 0,
|
||||
"InferSpmd's Input should appear before Attributes.");
|
||||
const std::pair<int, int> range = ctx.InputRangeAt(in_idx);
|
||||
const DistMetaTensor& arg = ctx.InputAt(range.first);
|
||||
return InferSpmdFnCallHelper<Tail...>::template Call<in_idx + 1,
|
||||
attr_idx>(
|
||||
ctx, pargs..., arg);
|
||||
}
|
||||
};
|
||||
|
||||
// for vector slot
|
||||
template <typename... Tail>
|
||||
struct InferSpmdFnCallHelper<const std::vector<const DistMetaTensor*>&,
|
||||
Tail...> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx, PreviousArgs&... pargs) {
|
||||
static_assert(attr_idx == 0,
|
||||
"InferSpmd's Input should appear before Attributes.");
|
||||
|
||||
const std::pair<int, int> range = ctx.InputRangeAt(in_idx);
|
||||
std::vector<const DistMetaTensor*> arg =
|
||||
ctx.InputsBetween(range.first, range.second);
|
||||
return InferSpmdFnCallHelper<Tail...>::template Call<in_idx + 1,
|
||||
attr_idx>(
|
||||
ctx, pargs..., arg);
|
||||
}
|
||||
};
|
||||
|
||||
// direct vector
|
||||
template <typename... Tail>
|
||||
struct InferSpmdFnCallHelper<const std::vector<DistMetaTensor>&, Tail...> {
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs>
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx, PreviousArgs&... pargs) {
|
||||
static_assert(attr_idx == 0,
|
||||
"InferSpmd's Input should appear before Attributes.");
|
||||
// TODO(liuzhenhai): parse input list as vector directly
|
||||
const std::pair<int, int> range = ctx.InputRangeAt(in_idx);
|
||||
std::vector<const DistMetaTensor*> tmp_arg =
|
||||
ctx.InputsBetween(range.first, range.second);
|
||||
std::vector<DistMetaTensor> arg;
|
||||
std::transform(tmp_arg.begin(),
|
||||
tmp_arg.end(),
|
||||
std::back_inserter(arg),
|
||||
[](const DistMetaTensor* arg_ptr) { return *arg_ptr; });
|
||||
return InferSpmdFnCallHelper<Tail...>::template Call<in_idx + 1,
|
||||
attr_idx>(
|
||||
ctx, pargs..., arg);
|
||||
}
|
||||
};
|
||||
|
||||
#define PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(attr_type) \
|
||||
template <typename... Tail> \
|
||||
struct InferSpmdFnCallHelper<attr_type, Tail...> { \
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs> \
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx, \
|
||||
PreviousArgs&... pargs) { \
|
||||
attr_type arg = ctx.AttrAt<attr_type>(attr_idx); \
|
||||
return InferSpmdFnCallHelper<Tail...>::template Call<in_idx, \
|
||||
attr_idx + 1>( \
|
||||
ctx, pargs..., arg); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_CONST_ATTRIBUTE_REF(attr_type) \
|
||||
template <typename... Tail> \
|
||||
struct InferSpmdFnCallHelper<const attr_type&, Tail...> { \
|
||||
template <int in_idx, int attr_idx, typename... PreviousArgs> \
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx, \
|
||||
PreviousArgs&... pargs) { \
|
||||
attr_type arg = ctx.AttrAt<attr_type>(attr_idx); \
|
||||
return InferSpmdFnCallHelper<Tail...>::template Call<in_idx, \
|
||||
attr_idx + 1>( \
|
||||
ctx, pargs..., arg); \
|
||||
} \
|
||||
}
|
||||
|
||||
// TODO(chenweihang): support other attr type later as needed
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(bool);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(int);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(float);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(double);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(int64_t);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_ATTRIBUTE(DataType);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_CONST_ATTRIBUTE_REF(std::vector<int>);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_CONST_ATTRIBUTE_REF(
|
||||
std::vector<int64_t>);
|
||||
PD_SPECIALIZE_InferSpmdFnCallHelper_FOR_CONST_ATTRIBUTE_REF(std::string);
|
||||
|
||||
/* End case */
|
||||
template <typename T>
|
||||
struct InferSpmdFnCallHelper<InferSpmdTypeTag<T>> {
|
||||
template <int in_idx, int attr_idx>
|
||||
static SpmdInfo Call(const InferSpmdContext& ctx UNUSED, Args&... args) {
|
||||
return infer_spmd_fn(args...);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class SpmdRule {
|
||||
public:
|
||||
explicit SpmdRule(InferSpmdFn forward_fn)
|
||||
: forward_fn_(forward_fn), backward_fn_(nullptr) {}
|
||||
|
||||
SpmdRule(InferSpmdFn forward_fn, InferSpmdFn backward_fn)
|
||||
: forward_fn_(forward_fn), backward_fn_(backward_fn) {}
|
||||
|
||||
SpmdInfo InferForward(const InferSpmdContext& ctx) const {
|
||||
PADDLE_ENFORCE_NE(forward_fn_,
|
||||
nullptr,
|
||||
common::errors::NotFound(
|
||||
"Current SpmdRule's forward function is not "
|
||||
"found, Please make sure "
|
||||
"that you have registered the rule correctly."));
|
||||
return forward_fn_(ctx);
|
||||
}
|
||||
|
||||
SpmdInfo InferBackward(const InferSpmdContext& ctx) const {
|
||||
PADDLE_ENFORCE_NE(backward_fn_,
|
||||
nullptr,
|
||||
common::errors::NotFound(
|
||||
"Current SpmdRule's backward function is not "
|
||||
"found, Please make sure "
|
||||
"that you have registered the rule correctly."));
|
||||
return backward_fn_(ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
InferSpmdFn forward_fn_;
|
||||
InferSpmdFn backward_fn_;
|
||||
};
|
||||
|
||||
// SpmdRuleFactory manage the spmd rules and cache the propagate results
|
||||
// TODO(chenweihang): Add spmd caching impl later
|
||||
class PADDLE_API SpmdRuleFactory {
|
||||
public:
|
||||
static SpmdRuleFactory& Instance();
|
||||
|
||||
bool ContainsSpmdRule(const std::string& kernel_name) const;
|
||||
|
||||
int InsertSpmdRule(std::string kernel_name, SpmdRule rule);
|
||||
|
||||
const SpmdRule& GetSpmdRule(const std::string& kernel_name) const;
|
||||
|
||||
private:
|
||||
SpmdRuleFactory() = default;
|
||||
|
||||
paddle::flat_hash_map<std::string, SpmdRule> spmd_rule_map_;
|
||||
|
||||
DISABLE_COPY_AND_ASSIGN(SpmdRuleFactory);
|
||||
};
|
||||
|
||||
#define PD_REGISTER_SPMD_RULE(kernel_name, ...) \
|
||||
UNUSED static int ___registrar_spmd_rule_for_##kernel_name = \
|
||||
::phi::distributed::SpmdRuleFactory::Instance().InsertSpmdRule( \
|
||||
#kernel_name, ::phi::distributed::SpmdRule(__VA_ARGS__));
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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/core/distributed/auto_parallel/placement_types.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
int64_t DistTensorMeta::num_shard() const {
|
||||
int64_t num_shard = 1;
|
||||
const auto& mesh_shape = process_mesh_->shape();
|
||||
for (size_t i = 0; i < placements_.size(); i++) {
|
||||
if (placements_[i]->is_shard()) {
|
||||
num_shard *= mesh_shape[i];
|
||||
}
|
||||
}
|
||||
return num_shard;
|
||||
}
|
||||
|
||||
std::vector<int64_t> DistTensorMeta::dim_mapping() const {
|
||||
int64_t ndim = dims().size();
|
||||
std::vector<int64_t> dim_map(ndim, -1);
|
||||
for (size_t i = 0; i < placements_.size(); i++) {
|
||||
auto& placement = placements_[i];
|
||||
if (placement->is_shard()) {
|
||||
auto shard_dim = dynamic_cast<const Shard&>(*placement).get_dim();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
dim_map[shard_dim],
|
||||
-1,
|
||||
common::errors::InvalidArgument(
|
||||
"Tensor dim %lld is already sharded on mesh dim %lld,"
|
||||
" DistTensor operator implementation does not support things "
|
||||
"like hybrid"
|
||||
" sharding strategies yet (i.e. [Shard(0), Shard(0)])",
|
||||
shard_dim,
|
||||
dim_map[shard_dim]));
|
||||
dim_map[shard_dim] = i; // NOLINT
|
||||
}
|
||||
}
|
||||
return dim_map;
|
||||
}
|
||||
|
||||
bool DistTensorMeta::is_replicated() const {
|
||||
return std::all_of(placements_.cbegin(),
|
||||
placements_.cend(),
|
||||
[](const auto& p) { return p->is_replicated(); });
|
||||
}
|
||||
|
||||
bool equal_placements(const Placements& a, const Placements& b) {
|
||||
if (a.size() != b.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < a.size(); ++i) {
|
||||
if (*a[i] != *b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
phi::distributed::Placements cvt_dim_map_to_placements(
|
||||
const ProcessMesh& process_mesh,
|
||||
const std::vector<int64_t>& dim_mapping,
|
||||
const paddle::flat_hash_map<int64_t, phi::ReduceType>& partial_status) {
|
||||
phi::distributed::Placements placements;
|
||||
placements.resize(process_mesh.ndim(),
|
||||
std::make_shared<phi::distributed::Replicate>());
|
||||
|
||||
for (const auto& pair : partial_status) {
|
||||
placements[pair.first] =
|
||||
std::make_shared<phi::distributed::Partial>(pair.second);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < dim_mapping.size(); ++i) {
|
||||
auto& mesh_id = dim_mapping[i];
|
||||
if (mesh_id >= 0) {
|
||||
auto& p = placements[mesh_id];
|
||||
if (p->is_shard()) {
|
||||
PADDLE_THROW(common::errors::PreconditionNotMet(
|
||||
"ProcessMesh dimension can't be mapped to two dimension of the "
|
||||
"same tensor: {%d} and {%d}",
|
||||
i,
|
||||
dynamic_cast<phi::distributed::Shard&>(*p).get_dim()));
|
||||
} else if (p->is_partial()) {
|
||||
PADDLE_THROW(common::errors::PreconditionNotMet(
|
||||
"ProcessMesh dimension {%d} cannot be both shard and partial!",
|
||||
mesh_id));
|
||||
}
|
||||
placements[mesh_id] = std::make_shared<phi::distributed::Shard>(i);
|
||||
}
|
||||
}
|
||||
return placements;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,305 @@
|
||||
// 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.
|
||||
|
||||
// This API design is inspired by:
|
||||
// https://github.com/pytorch/pytorch/blob/main/torch/distributed/_tensor/placement_types.py
|
||||
// Git commit hash: 52e2b87d00ed527dc7f990d1a7a4c5498f99c513
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/common/reduce_type.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/tensor_meta.h"
|
||||
#include "paddle/utils/flat_hash_map.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class Placement {
|
||||
public:
|
||||
virtual ~Placement() = default;
|
||||
|
||||
virtual bool is_shard(std::optional<int> dim = std::nullopt) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool is_replicated() const { return false; }
|
||||
|
||||
virtual bool is_partial() const { return false; }
|
||||
|
||||
virtual size_t hash() const { return 0; }
|
||||
|
||||
virtual std::string to_string() const { return ""; }
|
||||
|
||||
virtual bool operator==(const Placement& other) const {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Equal function is not implemented yet in Placement."));
|
||||
}
|
||||
|
||||
virtual bool operator!=(const Placement& other) const {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Not Equal function is not implemented yet in Placement."));
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Placement& p) {
|
||||
os << p.to_string();
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
class Shard : public Placement {
|
||||
public:
|
||||
explicit Shard(int dim) : dim_(dim) {}
|
||||
|
||||
Shard(int dim, int split_factor) : dim_(dim), split_factor_(split_factor) {}
|
||||
|
||||
bool is_shard(std::optional<int> dim = std::nullopt) const override {
|
||||
if (dim && *dim == this->dim_) {
|
||||
return true;
|
||||
} else {
|
||||
return !dim.has_value();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Placement& other) const override {
|
||||
const Shard* other_shard = dynamic_cast<const Shard*>(&other);
|
||||
if (!other_shard) return false;
|
||||
if (other_shard->get_co_shard_order() != 0) return false;
|
||||
return this->dim_ == other_shard->dim_ &&
|
||||
this->split_factor_ == other_shard->split_factor_;
|
||||
}
|
||||
|
||||
bool operator!=(const Placement& other) const override {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
std::size_t hash() const override {
|
||||
return std::hash<std::string>{}(to_string());
|
||||
}
|
||||
|
||||
int get_dim() const { return dim_; }
|
||||
|
||||
virtual int get_co_shard_order() const { return 0; }
|
||||
|
||||
void set_split_factor(int64_t sf) { split_factor_ = sf; }
|
||||
|
||||
int get_split_factor() const { return split_factor_; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Shard& p) {
|
||||
os << p.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string to_string() const override {
|
||||
std::stringstream ss;
|
||||
ss << "Shard(dim=" << std::to_string(dim_);
|
||||
if (split_factor_ != 1) {
|
||||
ss << ", split_factor=" << std::to_string(split_factor_);
|
||||
}
|
||||
ss << ")";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<Shard> copy() const {
|
||||
return std::make_shared<Shard>(*this);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<Shard> deepcopy() const {
|
||||
return std::make_shared<Shard>(*this);
|
||||
}
|
||||
|
||||
protected:
|
||||
int dim_;
|
||||
int split_factor_ = 1;
|
||||
};
|
||||
|
||||
class CoShard : public Shard {
|
||||
public:
|
||||
CoShard(int64_t dim, int64_t co_shard_order)
|
||||
: Shard(dim, 1), co_shard_order_(co_shard_order) {}
|
||||
|
||||
int get_co_shard_order() const override { return co_shard_order_; }
|
||||
|
||||
std::string to_string() const override {
|
||||
std::stringstream ss;
|
||||
ss << "Shard(dim=" << std::to_string(dim_);
|
||||
ss << ", shard_order=" << std::to_string(co_shard_order_) << ")";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const CoShard& p) {
|
||||
os << p.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::shared_ptr<Shard> copy() const override {
|
||||
return std::make_shared<CoShard>(*this);
|
||||
}
|
||||
|
||||
std::shared_ptr<Shard> deepcopy() const override {
|
||||
return std::make_shared<CoShard>(*this);
|
||||
}
|
||||
|
||||
bool operator==(const Placement& other) const override {
|
||||
if (const CoShard* other_coshard = dynamic_cast<const CoShard*>(&other)) {
|
||||
return this->dim_ == other_coshard->dim_ &&
|
||||
this->split_factor_ == other_coshard->split_factor_ &&
|
||||
this->co_shard_order_ == other_coshard->co_shard_order_;
|
||||
}
|
||||
if (const Shard* other_shard = dynamic_cast<const Shard*>(&other)) {
|
||||
return this->co_shard_order_ == 0 &&
|
||||
this->dim_ == other_shard->get_dim() &&
|
||||
this->split_factor_ == other_shard->get_split_factor();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const Placement& other) const override {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
std::size_t hash() const override {
|
||||
std::stringstream ss;
|
||||
ss << "Shard(dim=" << std::to_string(dim_);
|
||||
if (split_factor_ != 1) {
|
||||
ss << ", split_factor=" << std::to_string(split_factor_);
|
||||
}
|
||||
if (co_shard_order_ != 0) {
|
||||
ss << ", shard_order=" << std::to_string(co_shard_order_);
|
||||
}
|
||||
ss << ")";
|
||||
return std::hash<std::string>{}(ss.str());
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t co_shard_order_ = 0;
|
||||
};
|
||||
|
||||
class Replicate : public Placement {
|
||||
public:
|
||||
bool is_replicated() const override { return true; }
|
||||
|
||||
bool operator==(const Placement& other) const override {
|
||||
return dynamic_cast<const Replicate*>(&other) != nullptr;
|
||||
}
|
||||
|
||||
bool operator!=(const Placement& other) const override {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
std::size_t hash() const override {
|
||||
return std::hash<std::string>{}(to_string());
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Replicate& p) {
|
||||
os << p.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string to_string() const override { return "Replicate()"; }
|
||||
};
|
||||
|
||||
class Partial : public Placement {
|
||||
public:
|
||||
explicit Partial(ReduceType reduce_type) : reduce_type_(reduce_type) {}
|
||||
bool is_partial() const override { return true; }
|
||||
ReduceType get_reduce_type() const { return reduce_type_; }
|
||||
|
||||
bool operator==(const Placement& other) const override {
|
||||
const Partial* other_partial = dynamic_cast<const Partial*>(&other);
|
||||
return other_partial && this->reduce_type_ == other_partial->reduce_type_;
|
||||
}
|
||||
|
||||
bool operator!=(const Placement& other) const override {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
std::size_t hash() const override {
|
||||
return std::hash<std::string>{}(to_string());
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Partial& p) {
|
||||
os << p.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string to_string() const override {
|
||||
return "Partial(reduce_type=" +
|
||||
std::string(ReduceTypeStrings[static_cast<int>(reduce_type_)]) + ")";
|
||||
}
|
||||
|
||||
private:
|
||||
ReduceType reduce_type_;
|
||||
};
|
||||
|
||||
using Placements = std::vector<std::shared_ptr<Placement>>;
|
||||
class DistTensorMeta : public std::enable_shared_from_this<DistTensorMeta> {
|
||||
public:
|
||||
DistTensorMeta(const ProcessMesh& process_mesh,
|
||||
const Placements& placements,
|
||||
const DenseTensorMeta& tensor_meta)
|
||||
: process_mesh_(std::make_shared<const ProcessMesh>(process_mesh)),
|
||||
placements_(placements),
|
||||
tensor_meta_(std::make_shared<const DenseTensorMeta>(tensor_meta)) {}
|
||||
|
||||
DistTensorMeta() = default;
|
||||
|
||||
const DDim& dims() const { return tensor_meta_->dims; }
|
||||
|
||||
const ProcessMesh& process_mesh() const { return *process_mesh_; }
|
||||
|
||||
const Placements& placements() const { return placements_; }
|
||||
|
||||
int64_t num_shard() const;
|
||||
|
||||
std::vector<int64_t> dim_mapping() const;
|
||||
|
||||
bool is_replicated() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<const ProcessMesh> process_mesh_;
|
||||
Placements placements_;
|
||||
std::shared_ptr<const DenseTensorMeta> tensor_meta_;
|
||||
};
|
||||
|
||||
PADDLE_API bool equal_placements(const Placements& a, const Placements& b);
|
||||
|
||||
PADDLE_API phi::distributed::Placements cvt_dim_map_to_placements(
|
||||
const ProcessMesh& process_mesh,
|
||||
const std::vector<int64_t>& dim_mapping,
|
||||
const paddle::flat_hash_map<int64_t, phi::ReduceType>& partial_status);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<phi::distributed::Placement> {
|
||||
std::size_t operator()(const phi::distributed::Placement& p) const {
|
||||
return p.hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
@@ -0,0 +1,241 @@
|
||||
/* 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. */
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/proto_helper.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
using phi::distributed::auto_parallel::has_duplicates;
|
||||
using phi::distributed::auto_parallel::ProcessMeshProto;
|
||||
using phi::distributed::auto_parallel::str_join;
|
||||
|
||||
ProcessMesh::ProcessMesh(const std::vector<int64_t> &shape,
|
||||
const std::vector<int64_t> &process_ids,
|
||||
const std::vector<std::string> &dim_names) {
|
||||
shape_ = shape;
|
||||
int64_t size = this->size();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
size,
|
||||
process_ids.size(),
|
||||
errors::InvalidArgument(
|
||||
"The size of this process mesh must be equal to the size of its "
|
||||
"process ids, but got mesh size %d and process id size %d.",
|
||||
size,
|
||||
process_ids.size()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
has_duplicates(process_ids),
|
||||
false,
|
||||
errors::InvalidArgument("The process ids [%s] must be unique.",
|
||||
str_join(process_ids_)));
|
||||
process_ids_ = process_ids;
|
||||
|
||||
PADDLE_ENFORCE_EQ(
|
||||
shape_.size(),
|
||||
dim_names.size(),
|
||||
errors::InvalidArgument(
|
||||
"The size of mesh shape must be equal to the size of the dimension "
|
||||
"names, but got shape size %d and dimension name size %d.",
|
||||
shape_.size(),
|
||||
dim_names.size()));
|
||||
PADDLE_ENFORCE_EQ(has_duplicates(dim_names),
|
||||
false,
|
||||
errors::InvalidArgument(
|
||||
"The names [%s] of each dimension must be unique.",
|
||||
str_join(dim_names)));
|
||||
dim_names_ = dim_names;
|
||||
}
|
||||
|
||||
int64_t ProcessMesh::size() const {
|
||||
if (shape_.empty()) return 0;
|
||||
int64_t size = 1;
|
||||
for (const int64_t dim_size : shape_) size *= dim_size;
|
||||
return size;
|
||||
}
|
||||
|
||||
bool ProcessMesh::contains(int64_t process_id) const {
|
||||
auto result =
|
||||
std::find(std::begin(process_ids_), std::end(process_ids_), process_id);
|
||||
if (result != std::end(process_ids_)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ProcessMesh::to_string() const {
|
||||
std::string mesh_str = "{shape: [" + str_join(shape_) + "], ";
|
||||
mesh_str += "process_ids: [" + str_join(process_ids_) + "], ";
|
||||
mesh_str += "dim_names: [" + str_join(dim_names_) + "]}";
|
||||
return mesh_str;
|
||||
}
|
||||
|
||||
ProcessMesh ProcessMesh::from_proto(const ProcessMeshProto &proto) {
|
||||
ProcessMesh mesh;
|
||||
|
||||
mesh.shape_.resize(proto.shape_size());
|
||||
for (int i = 0; i < proto.shape_size(); ++i) {
|
||||
mesh.shape_[i] = proto.shape(i);
|
||||
}
|
||||
|
||||
mesh.process_ids_.resize(proto.process_ids_size());
|
||||
for (int i = 0; i < proto.process_ids_size(); ++i) {
|
||||
mesh.process_ids_[i] = proto.process_ids(i);
|
||||
}
|
||||
|
||||
mesh.dim_names_.resize(proto.dim_names_size());
|
||||
for (int i = 0; i < proto.dim_names_size(); ++i) {
|
||||
mesh.dim_names_[i] = proto.dim_names(i);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void ProcessMesh::to_proto(ProcessMeshProto *proto) const {
|
||||
for (const auto &i : shape_) {
|
||||
proto->add_shape(i);
|
||||
}
|
||||
|
||||
for (const auto &i : process_ids_) {
|
||||
proto->add_process_ids(i);
|
||||
}
|
||||
|
||||
for (const auto &i : dim_names_) {
|
||||
proto->add_dim_names(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const ProcessMesh &lhs, const ProcessMesh &rhs) {
|
||||
if (lhs.shape() != rhs.shape() || lhs.process_ids() != rhs.process_ids()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mesh_equal_ignore_shape1(const ProcessMesh &a,
|
||||
const ProcessMesh &b,
|
||||
int split_dim) {
|
||||
if (a == b) {
|
||||
return true;
|
||||
}
|
||||
if (a.process_ids() != b.process_ids()) {
|
||||
return false;
|
||||
}
|
||||
std::vector<int64_t> a_shape = a.shape();
|
||||
std::vector<int64_t> b_shape = b.shape();
|
||||
if (a_shape[split_dim] != 1) {
|
||||
return false;
|
||||
}
|
||||
a_shape.erase(a_shape.begin() + split_dim);
|
||||
return a_shape == b_shape;
|
||||
}
|
||||
|
||||
std::vector<ProcessMesh> SplitMesh(const ProcessMesh &mesh, int axis) {
|
||||
std::vector<int64_t> mesh_shape = mesh.shape();
|
||||
std::vector<int64_t> process_ids = mesh.process_ids();
|
||||
std::vector<ProcessMesh> result;
|
||||
|
||||
int64_t total_elements = process_ids.size();
|
||||
|
||||
int64_t num_splits = mesh_shape[axis];
|
||||
int64_t prod_before = std::accumulate(mesh_shape.begin(),
|
||||
mesh_shape.begin() + axis,
|
||||
1,
|
||||
std::multiplies<int64_t>());
|
||||
int64_t prod_after = std::accumulate(mesh_shape.begin() + axis + 1,
|
||||
mesh_shape.end(),
|
||||
1,
|
||||
std::multiplies<int64_t>());
|
||||
|
||||
for (int i = 0; i < num_splits; ++i) {
|
||||
std::vector<int64_t> new_shape = mesh_shape;
|
||||
new_shape[axis] = 1;
|
||||
|
||||
std::vector<int64_t> new_process_ids;
|
||||
for (int64_t j = 0; j < prod_before; ++j) {
|
||||
for (int64_t k = 0; k < prod_after; ++k) {
|
||||
int64_t index = j * mesh_shape[axis] * prod_after + i * prod_after + k;
|
||||
if (index < total_elements) {
|
||||
new_process_ids.push_back(process_ids[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(
|
||||
ProcessMesh(new_shape, new_process_ids, mesh.dim_names()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int SubMeshDim(const ProcessMesh &global_mesh, const ProcessMesh &sub_mesh) {
|
||||
std::set<int64_t> global_ids(global_mesh.process_ids().begin(),
|
||||
global_mesh.process_ids().end());
|
||||
std::set<int64_t> sub_ids(sub_mesh.process_ids().begin(),
|
||||
sub_mesh.process_ids().end());
|
||||
if (!std::includes(
|
||||
global_ids.begin(), global_ids.end(), sub_ids.begin(), sub_ids.end()))
|
||||
return -1;
|
||||
|
||||
int sub_dim = -1;
|
||||
std::vector<int64_t> global_shape = global_mesh.shape();
|
||||
std::vector<int64_t> sub_shape = sub_mesh.shape();
|
||||
|
||||
if (global_mesh.ndim() == sub_mesh.ndim() + 1) {
|
||||
// for the case that the `1` is not explicitly specified in the shape
|
||||
// e.g.
|
||||
// global_mesh: shape = [2,3], process_ids = [0,1,2,3,4,5]
|
||||
// sub_mesh: shape = [3], process_ids = [0,1,2]
|
||||
int global_ndim = global_mesh.ndim();
|
||||
for (int i = 0; i < global_ndim - 1; ++i) {
|
||||
std::vector<ProcessMesh> sub_meshes = SplitMesh(global_mesh, i);
|
||||
for (const ProcessMesh &mesh : sub_meshes) {
|
||||
if (mesh_equal_ignore_shape1(mesh, sub_mesh, i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sub_dim;
|
||||
} else if (global_mesh.ndim() != sub_mesh.ndim()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// e.g.
|
||||
// global_mesh: shape = [1,2], process_ids = [0,1]; sub_mesh: shape = [1, 1],
|
||||
// process_ids = [0] global_mesh: shape = [2,2], process_ids = [0,1,2,3];
|
||||
// sub_mesh: shape = [2, 1], process_ids = [0, 2]
|
||||
auto it =
|
||||
std::mismatch(sub_shape.begin(), sub_shape.end(), global_shape.begin());
|
||||
if (it.first == sub_shape.end()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub_dim = it.first - sub_shape.begin();
|
||||
std::vector<ProcessMesh> sub_meshes = SplitMesh(global_mesh, sub_dim);
|
||||
if (std::find(sub_meshes.begin(), sub_meshes.end(), sub_mesh) !=
|
||||
sub_meshes.end()) {
|
||||
return sub_dim;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,114 @@
|
||||
/* 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. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/device_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
namespace auto_parallel {
|
||||
class ProcessMeshProto;
|
||||
}
|
||||
|
||||
class PADDLE_API ProcessMesh {
|
||||
public:
|
||||
ProcessMesh() = default;
|
||||
|
||||
ProcessMesh(const std::vector<int64_t>& shape,
|
||||
const std::vector<int64_t>& process_ids,
|
||||
const std::vector<std::string>& dim_names);
|
||||
|
||||
const std::vector<int64_t>& shape() const { return shape_; }
|
||||
|
||||
const std::vector<int64_t>& process_ids() const { return process_ids_; }
|
||||
|
||||
const std::vector<std::string>& dim_names() const { return dim_names_; }
|
||||
|
||||
int64_t size() const;
|
||||
|
||||
int64_t ndim() const { return shape_.size(); }
|
||||
|
||||
int64_t dim_size(int64_t dim) const {
|
||||
int64_t cdim = auto_parallel::canonical_dim(dim, shape_.size());
|
||||
return shape_[cdim];
|
||||
}
|
||||
|
||||
int64_t dim_size(const std::string& dim_name) const {
|
||||
for (std::size_t i = 0; i < dim_names_.size(); ++i) {
|
||||
if (dim_names_[i] == dim_name) {
|
||||
return shape_[i];
|
||||
}
|
||||
}
|
||||
PADDLE_THROW(errors::InvalidArgument(
|
||||
"Cannot find the dimension of %s in this process mesh.", dim_name));
|
||||
}
|
||||
|
||||
bool empty() const { return (shape_.empty() || process_ids_.empty()); }
|
||||
bool contains(int64_t process_id) const;
|
||||
|
||||
size_t hash() const { return std::hash<std::string>{}(to_string()); }
|
||||
|
||||
// ProcessMesh from_string(const std::string& mesh_str);
|
||||
std::string to_string() const;
|
||||
|
||||
static ProcessMesh from_proto(const auto_parallel::ProcessMeshProto& proto);
|
||||
void to_proto(auto_parallel::ProcessMeshProto* proto) const;
|
||||
|
||||
private:
|
||||
std::vector<int64_t> shape_;
|
||||
std::vector<int64_t> process_ids_;
|
||||
std::vector<std::string> dim_names_;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const ProcessMesh& obj) {
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
PADDLE_API bool operator==(const ProcessMesh& lhs, const ProcessMesh& rhs);
|
||||
|
||||
inline bool operator!=(const ProcessMesh& lhs, const ProcessMesh& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
// split the mesh into sub-meshes at the given axis
|
||||
PADDLE_API std::vector<ProcessMesh> SplitMesh(const ProcessMesh& mesh,
|
||||
int axis);
|
||||
|
||||
// return which dimension that the sub_mesh is split from the global_mesh,
|
||||
// if sub_mesh is not a subset of global_mesh, return -1
|
||||
PADDLE_API int SubMeshDim(const ProcessMesh& global_mesh,
|
||||
const ProcessMesh& sub_mesh);
|
||||
|
||||
// when the shapes of two meshes are different and their process_ids
|
||||
// are the same, check whether the only difference is that mesh 'a'
|
||||
// has an additional '1' on the split dim of its shape.
|
||||
// e.g. a.shape = [2], b.shape = [2, 1], and the process_ids are the
|
||||
// same, then they are equal.
|
||||
PADDLE_API bool mesh_equal_ignore_shape1(const ProcessMesh& a,
|
||||
const ProcessMesh& b,
|
||||
int split_dim);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2024 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/core/distributed/auto_parallel/proto_helper.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/auto_parallel.pb.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/device_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_mapper.h"
|
||||
|
||||
#define TO_PROTO_HELPER(object, proto_type) \
|
||||
proto_type proto; \
|
||||
object.to_proto(&proto); \
|
||||
return proto
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
auto_parallel::TensorDistAttrProto to_proto(const TensorDistAttr& dist_attr) {
|
||||
TO_PROTO_HELPER(dist_attr, auto_parallel::TensorDistAttrProto);
|
||||
}
|
||||
|
||||
auto_parallel::ProcessMeshProto to_proto(const ProcessMesh& process_mesh) {
|
||||
TO_PROTO_HELPER(process_mesh, auto_parallel::ProcessMeshProto);
|
||||
}
|
||||
|
||||
auto_parallel::DeviceCapabilityProto to_proto(
|
||||
const auto_parallel::DeviceCapability& device_capability) {
|
||||
TO_PROTO_HELPER(device_capability, auto_parallel::DeviceCapabilityProto);
|
||||
}
|
||||
|
||||
auto_parallel::DeviceProto to_proto(const auto_parallel::Device& device) {
|
||||
TO_PROTO_HELPER(device, auto_parallel::DeviceProto);
|
||||
}
|
||||
|
||||
auto_parallel::LinkCapabilityProto to_proto(
|
||||
const auto_parallel::LinkCapability& link_capability) {
|
||||
TO_PROTO_HELPER(link_capability, auto_parallel::LinkCapabilityProto);
|
||||
}
|
||||
|
||||
auto_parallel::LinkProto to_proto(const auto_parallel::Link& link) {
|
||||
TO_PROTO_HELPER(link, auto_parallel::LinkProto);
|
||||
}
|
||||
|
||||
auto_parallel::DeviceMeshProto to_proto(
|
||||
const auto_parallel::DeviceMesh& device_mesh) {
|
||||
TO_PROTO_HELPER(device_mesh, auto_parallel::DeviceMeshProto);
|
||||
}
|
||||
|
||||
auto_parallel::DistributedMapperProto to_proto(
|
||||
const auto_parallel::DistributedMapper& dist_mapper) {
|
||||
TO_PROTO_HELPER(dist_mapper, auto_parallel::DistributedMapperProto);
|
||||
}
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2024 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.
|
||||
|
||||
#pragma once
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/auto_parallel.pb.h"
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
class TensorDistAttr;
|
||||
class ProcessMesh;
|
||||
namespace auto_parallel {
|
||||
struct DeviceCapability;
|
||||
class Device;
|
||||
struct LinkCapability;
|
||||
class Link;
|
||||
class DeviceMesh;
|
||||
class DistributedMapper;
|
||||
} // namespace auto_parallel
|
||||
PADDLE_API auto_parallel::TensorDistAttrProto to_proto(
|
||||
const TensorDistAttr& dist_attr);
|
||||
PADDLE_API auto_parallel::ProcessMeshProto to_proto(
|
||||
const ProcessMesh& dist_attr);
|
||||
|
||||
PADDLE_API auto_parallel::DeviceCapabilityProto to_proto(
|
||||
const auto_parallel::DeviceCapability& device_capability);
|
||||
PADDLE_API auto_parallel::DeviceProto to_proto(
|
||||
const auto_parallel::Device& device);
|
||||
PADDLE_API auto_parallel::LinkCapabilityProto to_proto(
|
||||
const auto_parallel::LinkCapability& link_capability);
|
||||
PADDLE_API auto_parallel::LinkProto to_proto(const auto_parallel::Link& link);
|
||||
PADDLE_API auto_parallel::DeviceMeshProto to_proto(
|
||||
const auto_parallel::DeviceMesh& link);
|
||||
PADDLE_API auto_parallel::DistributedMapperProto to_proto(
|
||||
const auto_parallel::DistributedMapper& dist_mapper);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,18 @@
|
||||
collect_srcs(
|
||||
core_srcs
|
||||
SRCS
|
||||
reshard_utils.cc
|
||||
reshard_function.cc
|
||||
r_to_s_reshard_function.cc
|
||||
s_to_r_reshard_function.cc
|
||||
r_to_p_reshard_function.cc
|
||||
p_to_r_reshard_function.cc
|
||||
s_to_s_reshard_function.cc
|
||||
p_to_s_reshard_function.cc
|
||||
s_to_p_reshard_function.cc
|
||||
x_to_r_reshard_function.cc
|
||||
r_to_x_reshard_function.cc
|
||||
nd_mesh_reshard_function.cc
|
||||
same_status_reshard_function.cc
|
||||
global_and_sub_mesh_reshard_function.cc
|
||||
reshard_function_registry.cc)
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/global_and_sub_mesh_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/p_recv_kernel.h"
|
||||
#include "paddle/phi/kernels/p_send_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool GlobalToSubMeshReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const TensorDistAttr& in_dist_attr = in.dist_attr();
|
||||
|
||||
const ProcessMesh& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const ProcessMesh& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
int sub_mesh_dim = SubMeshDim(in_process_mesh, out_process_mesh);
|
||||
RESHARD_SHORTCUT_IF_FALSE(sub_mesh_dim != -1);
|
||||
// 1. the split dimension must be replicated
|
||||
// 2. out mesh is the value of a certain dimension of global mesh
|
||||
// e.g. global_mesh = [[1, 2], [3, 4]], out_mesh = [1, 2] or [3, 4]
|
||||
// global_mesh = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
||||
// out_mesh = [[1, 2], [3, 4]] or [[5, 6], [7, 8]]
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated(sub_mesh_dim));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalToSubMeshReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call GlobalToSubMeshReshardFunction Eval";
|
||||
const DenseTensor& in_dense_value = in.value();
|
||||
const ProcessMesh& out_process_mesh = out_dist_attr.process_mesh();
|
||||
if (IsCurRankInMesh(out_process_mesh)) {
|
||||
SetValue(out, in_dense_value);
|
||||
} else {
|
||||
*(out->unsafe_mutable_value()) =
|
||||
DenseTensor(std::make_shared<phi::Allocation>(
|
||||
nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
in.value().meta());
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool SubMeshToGlobalReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const TensorDistAttr& in_dist_attr = in.dist_attr();
|
||||
const ProcessMesh& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const ProcessMesh& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
int sub_mesh_dim = SubMeshDim(out_process_mesh, in_process_mesh);
|
||||
RESHARD_SHORTCUT_IF_FALSE(sub_mesh_dim != -1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated(sub_mesh_dim));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SubMeshToGlobalReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call SubMeshToGlobalReshardFunction Eval";
|
||||
const TensorDistAttr& in_dist_attr = in.dist_attr();
|
||||
const ProcessMesh& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const ProcessMesh& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
int sub_mesh_dim = SubMeshDim(out_process_mesh, in_process_mesh);
|
||||
std::vector<ProcessMesh> sub_process_meshes =
|
||||
SplitMesh(out_process_mesh, sub_mesh_dim);
|
||||
const std::vector<int64_t>& in_process_ids = in_process_mesh.process_ids();
|
||||
const std::vector<int64_t>& out_process_ids = out_process_mesh.process_ids();
|
||||
std::unordered_map<int64_t, std::vector<int64_t>> send2recv_map;
|
||||
std::unordered_map<int64_t, int64_t> recv2send_map;
|
||||
|
||||
for (const ProcessMesh& sub_mesh : sub_process_meshes) {
|
||||
if (mesh_equal_ignore_shape1(sub_mesh, in_process_mesh, sub_mesh_dim)) {
|
||||
continue;
|
||||
}
|
||||
const std::vector<int64_t>& sub_process_ids = sub_mesh.process_ids();
|
||||
for (size_t i = 0; i < sub_process_ids.size(); ++i) {
|
||||
int64_t send_id = in_process_ids[i];
|
||||
send2recv_map[send_id].push_back(sub_process_ids[i]);
|
||||
recv2send_map[sub_process_ids[i]] = send_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int64_t> all_process_ids =
|
||||
GetUnionProcessIds(in_process_ids, out_process_ids);
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
DataType dtype = in.dtype();
|
||||
if (IsCurRankInMesh(in_process_mesh)) {
|
||||
const DenseTensor& in_dense_value = in.value();
|
||||
std::vector<int64_t>& recv_vec = send2recv_map[cur_global_rank];
|
||||
for (int64_t recv_id : recv_vec) {
|
||||
auto relative_recv_rank = recv_id;
|
||||
for (size_t i = 0; i < all_process_ids.size(); ++i) {
|
||||
if (all_process_ids[i] == recv_id) {
|
||||
relative_recv_rank = i;
|
||||
}
|
||||
}
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PSendKernel,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
in_dense_value,
|
||||
relative_recv_rank, /*peer*/
|
||||
true /*dynamic_shape*/);
|
||||
}
|
||||
SetValue(out, in_dense_value);
|
||||
} else {
|
||||
int64_t send_id = recv2send_map[cur_global_rank];
|
||||
auto relative_send_rank = send_id;
|
||||
for (size_t i = 0; i < all_process_ids.size(); ++i) {
|
||||
if (all_process_ids[i] == send_id) {
|
||||
relative_send_rank = i;
|
||||
}
|
||||
}
|
||||
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PRecv,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
relative_send_rank, /*peer*/
|
||||
{} /*out_shape*/,
|
||||
true /*dynamic_shape*/,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API GlobalToSubMeshReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "GlobalToSubMeshReshardFunction"; }
|
||||
};
|
||||
|
||||
class PADDLE_API SubMeshToGlobalReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SubMeshToGlobalReshardFunction"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,550 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/nd_mesh_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/p_to_r_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/p_to_s_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_p_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_s_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/s_to_r_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
namespace {
|
||||
ProcessMesh GetSubProcessMesh(const ProcessMesh& mesh, int64_t axis) {
|
||||
int64_t shape_of_axis = mesh.dim_size(axis);
|
||||
std::vector<int64_t> shape = {shape_of_axis};
|
||||
std::vector<std::string> dim_names = {mesh.dim_names()[axis]};
|
||||
std::vector<int64_t> coord = GetCurRankCoordInMesh(mesh);
|
||||
|
||||
std::vector<int64_t> process_ids;
|
||||
for (int64_t i = 0; i < shape_of_axis; ++i) {
|
||||
coord[axis] = i;
|
||||
int64_t rank = 0;
|
||||
int64_t degree = 1;
|
||||
for (int64_t j = static_cast<int64_t>(coord.size() - 1); j >= 0; --j) {
|
||||
rank += coord[j] * degree;
|
||||
degree *= mesh.dim_size(j);
|
||||
}
|
||||
process_ids.emplace_back(mesh.process_ids()[rank]);
|
||||
}
|
||||
|
||||
ProcessMesh out_mesh(shape, process_ids, dim_names);
|
||||
return out_mesh;
|
||||
}
|
||||
|
||||
// Given the input two dist_attr, traversing from high-dimension axis to
|
||||
// low-dimension. Find and return the first different axis which is shard status
|
||||
// between these two. For example, the input two dims_mapping are [-1, 0, -1,
|
||||
// -1] and [-1, -1, 0, -1], the first diff shard axis is 2.
|
||||
int64_t FindFirstDiffShardAxis(const TensorDistAttr& in_dist_attr,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dims_mapping = in_dist_attr.multi_dims_mapping();
|
||||
const auto& out_dims_mapping = out_dist_attr.multi_dims_mapping();
|
||||
|
||||
VLOG(3) << "In find diff axis, in dim mapping "
|
||||
<< auto_parallel::str_join(in_dims_mapping) << ", out dim mapping "
|
||||
<< auto_parallel::str_join(out_dims_mapping);
|
||||
int64_t axis = -1;
|
||||
|
||||
for (int64_t i = static_cast<int64_t>(in_dims_mapping.size() - 1); i >= 0;
|
||||
--i) {
|
||||
if (in_dims_mapping[i] != out_dims_mapping[i]) {
|
||||
axis = i;
|
||||
break;
|
||||
}
|
||||
auto predicate = [&in_dist_attr, &out_dist_attr](int64_t mesh_dim) {
|
||||
if (in_dist_attr.get_split_factor(mesh_dim) !=
|
||||
out_dist_attr.get_split_factor(mesh_dim)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (std::any_of(
|
||||
in_dims_mapping[i].begin(), in_dims_mapping[i].end(), predicate)) {
|
||||
axis = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class ReshardContext final {
|
||||
public:
|
||||
ReshardContext(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out)
|
||||
: dev_ctx(dev_ctx), in(in), out_dist_attr(out_dist_attr), out(out) {}
|
||||
|
||||
TensorDistAttr CreateOneDimDistAttr(
|
||||
const ProcessMesh& sub_mesh,
|
||||
bool is_partial = false,
|
||||
std::optional<ReduceType> reduce_type = std::nullopt,
|
||||
std::optional<int64_t> cur_tensor_dim = std::nullopt,
|
||||
std::optional<int64_t> cur_mesh_split_factor = std::nullopt) const {
|
||||
TensorDistAttr dist_attr(common::vectorize(in.dims()));
|
||||
dist_attr.set_process_mesh(sub_mesh);
|
||||
|
||||
if (is_partial) {
|
||||
if (reduce_type) {
|
||||
dist_attr.set_partial_status(std::vector<int64_t>{0},
|
||||
reduce_type.value());
|
||||
} else {
|
||||
dist_attr.set_partial_status(std::vector<int64_t>{0});
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_tensor_dim) {
|
||||
auto dims_mapping = dist_attr.multi_dims_mapping();
|
||||
PADDLE_ENFORCE_GE(
|
||||
cur_tensor_dim.value(),
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"tensor dim should be greater than or equal to 0, but got %d.",
|
||||
cur_tensor_dim.value()));
|
||||
dims_mapping[cur_tensor_dim.value()] = {0};
|
||||
dist_attr.set_dims_mapping(dims_mapping);
|
||||
if (cur_mesh_split_factor) {
|
||||
dist_attr.set_split_factor(0, cur_mesh_split_factor.value());
|
||||
}
|
||||
}
|
||||
return dist_attr;
|
||||
}
|
||||
|
||||
ProcessMesh GetSubProcessMesh(int64_t axis) const {
|
||||
return phi::distributed::GetSubProcessMesh(out_dist_attr.process_mesh(),
|
||||
axis);
|
||||
}
|
||||
|
||||
DeviceContext* dev_ctx;
|
||||
const DistTensor& in;
|
||||
const TensorDistAttr& out_dist_attr;
|
||||
DistTensor* out;
|
||||
DistTensor tmp_result;
|
||||
};
|
||||
|
||||
template <typename ReshardFunc>
|
||||
class SingleDimReshardStrategy
|
||||
: public SameNdMeshReshardFunction::ReshardStrategy {
|
||||
public:
|
||||
SingleDimReshardStrategy(int64_t cur_tensor_dim,
|
||||
int64_t cur_mesh_dim,
|
||||
ReshardContext ctx)
|
||||
: cur_tensor_dim_(cur_tensor_dim),
|
||||
cur_mesh_dim_(cur_mesh_dim),
|
||||
ctx_(ctx) {}
|
||||
void Eval() override {
|
||||
auto cur_dist_attr = CalculateNewDistAttr();
|
||||
VLOG(3) << "New Dist Attr " << cur_dist_attr;
|
||||
auto sub_mesh = ctx_.GetSubProcessMesh(cur_mesh_dim_);
|
||||
VLOG(3) << "Get Sub Mesh " << sub_mesh;
|
||||
auto in_one_dim = CreateOneDimInDistAttr(sub_mesh);
|
||||
VLOG(3) << "One dim In Attr " << in_one_dim;
|
||||
auto out_one_dim = CreateOneDimOutDistAttr(sub_mesh);
|
||||
VLOG(3) << "One dim Out Attr " << out_one_dim;
|
||||
|
||||
SetDistProps(ctx_.out, in_one_dim);
|
||||
VLOG(3) << "Set One dim In Attr";
|
||||
ReshardFunc func;
|
||||
func.Eval(ctx_.dev_ctx, *ctx_.out, out_one_dim, &ctx_.tmp_result);
|
||||
VLOG(3) << "Finish reshard func.";
|
||||
SetValue(ctx_.out, ctx_.tmp_result.value());
|
||||
VLOG(3) << "Set local value";
|
||||
SetDistProps(ctx_.out, cur_dist_attr);
|
||||
VLOG(3) << "Set Cur Dist Attr";
|
||||
}
|
||||
|
||||
private:
|
||||
virtual TensorDistAttr CalculateNewDistAttr() const = 0;
|
||||
virtual TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const = 0;
|
||||
virtual TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const = 0;
|
||||
|
||||
protected:
|
||||
int64_t cur_tensor_dim_;
|
||||
int64_t cur_mesh_dim_;
|
||||
ReshardContext ctx_;
|
||||
};
|
||||
|
||||
class PartialToReplicate final
|
||||
: public SingleDimReshardStrategy<PToRReshardFunction> {
|
||||
private:
|
||||
using SingleDimReshardStrategy<PToRReshardFunction>::SingleDimReshardStrategy;
|
||||
|
||||
TensorDistAttr CalculateNewDistAttr() const override {
|
||||
auto real_out_attr = ctx_.out->dist_attr();
|
||||
real_out_attr.clean_partial_dims({cur_mesh_dim_});
|
||||
return real_out_attr;
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
auto input_reduce_type =
|
||||
ctx_.out->dist_attr().partial_status().at(cur_mesh_dim_);
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh, true, input_reduce_type);
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh);
|
||||
}
|
||||
};
|
||||
|
||||
class ShardToReplicate final
|
||||
: public SingleDimReshardStrategy<SToRReshardFunction> {
|
||||
private:
|
||||
using SingleDimReshardStrategy<SToRReshardFunction>::SingleDimReshardStrategy;
|
||||
|
||||
TensorDistAttr CalculateNewDistAttr() const override {
|
||||
auto real_out_attr = ctx_.out->dist_attr();
|
||||
std::vector<std::vector<int64_t>> real_dims_mapping =
|
||||
real_out_attr.multi_dims_mapping();
|
||||
real_dims_mapping[cur_tensor_dim_] = {};
|
||||
real_out_attr.set_dims_mapping(real_dims_mapping);
|
||||
real_out_attr.clear_split_factor(cur_mesh_dim_);
|
||||
return real_out_attr;
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
auto split_factor = ctx_.out->dist_attr().get_split_factor(cur_mesh_dim_);
|
||||
VLOG(3) << "In S To R, cur mesh dim is " << cur_mesh_dim_
|
||||
<< ", split factor is " << split_factor;
|
||||
return ctx_.CreateOneDimDistAttr(
|
||||
sub_mesh, false, std::nullopt, cur_tensor_dim_, split_factor);
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh);
|
||||
}
|
||||
};
|
||||
|
||||
class ReplicateToPartial final
|
||||
: public SingleDimReshardStrategy<RToPReshardFunction> {
|
||||
private:
|
||||
using SingleDimReshardStrategy<RToPReshardFunction>::SingleDimReshardStrategy;
|
||||
TensorDistAttr CalculateNewDistAttr() const override {
|
||||
TensorDistAttr real_out_dist_attr = ctx_.out->dist_attr();
|
||||
real_out_dist_attr.set_partial_status(std::vector<int64_t>{cur_mesh_dim_});
|
||||
return real_out_dist_attr;
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh);
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh, true);
|
||||
}
|
||||
};
|
||||
|
||||
class ReplicateToShard final
|
||||
: public SingleDimReshardStrategy<RToSReshardFunction> {
|
||||
private:
|
||||
using SingleDimReshardStrategy<RToSReshardFunction>::SingleDimReshardStrategy;
|
||||
|
||||
TensorDistAttr CalculateNewDistAttr() const override {
|
||||
TensorDistAttr real_out_dist_attr(ctx_.out->dist_attr());
|
||||
std::vector<std::vector<int64_t>> real_dims_mapping =
|
||||
real_out_dist_attr.multi_dims_mapping();
|
||||
real_dims_mapping[cur_tensor_dim_].push_back(cur_mesh_dim_);
|
||||
real_out_dist_attr.set_dims_mapping(real_dims_mapping);
|
||||
|
||||
auto split_factor = ctx_.out_dist_attr.get_split_factor(cur_mesh_dim_);
|
||||
real_out_dist_attr.set_split_factor(cur_mesh_dim_, split_factor);
|
||||
return real_out_dist_attr;
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh);
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
auto split_factor = ctx_.out_dist_attr.get_split_factor(cur_mesh_dim_);
|
||||
VLOG(3) << "In R to S mesh dim is " << cur_mesh_dim_ << ", split factor is "
|
||||
<< split_factor;
|
||||
return ctx_.CreateOneDimDistAttr(
|
||||
sub_mesh, false, std::nullopt, cur_tensor_dim_, split_factor);
|
||||
}
|
||||
};
|
||||
|
||||
class PartialToShard final
|
||||
: public SingleDimReshardStrategy<PToSReshardFunction> {
|
||||
private:
|
||||
using SingleDimReshardStrategy<PToSReshardFunction>::SingleDimReshardStrategy;
|
||||
|
||||
TensorDistAttr CalculateNewDistAttr() const override {
|
||||
TensorDistAttr real_out_dist_attr(ctx_.out->dist_attr());
|
||||
std::vector<std::vector<int64_t>> real_dims_mapping =
|
||||
real_out_dist_attr.multi_dims_mapping();
|
||||
real_dims_mapping[cur_tensor_dim_].push_back(cur_mesh_dim_);
|
||||
real_out_dist_attr.set_dims_mapping(real_dims_mapping);
|
||||
if (real_out_dist_attr.is_partial(cur_mesh_dim_)) {
|
||||
real_out_dist_attr.clean_partial_dims({cur_mesh_dim_});
|
||||
}
|
||||
|
||||
auto split_factor = ctx_.out_dist_attr.get_split_factor(cur_mesh_dim_);
|
||||
real_out_dist_attr.set_split_factor(cur_mesh_dim_, split_factor);
|
||||
return real_out_dist_attr;
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimInDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
auto input_reduce_type =
|
||||
ctx_.out->dist_attr().partial_status().at(cur_mesh_dim_);
|
||||
return ctx_.CreateOneDimDistAttr(sub_mesh, true, input_reduce_type);
|
||||
}
|
||||
|
||||
TensorDistAttr CreateOneDimOutDistAttr(
|
||||
const ProcessMesh& sub_mesh) const override {
|
||||
auto split_factor = ctx_.out_dist_attr.get_split_factor(cur_mesh_dim_);
|
||||
VLOG(3) << "In P to S mesh dim is " << cur_mesh_dim_ << ", split factor is "
|
||||
<< split_factor;
|
||||
return ctx_.CreateOneDimDistAttr(
|
||||
sub_mesh, false, std::nullopt, cur_tensor_dim_, split_factor);
|
||||
}
|
||||
};
|
||||
|
||||
void ProcessPartialToReplicated(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
TensorDistAttr out_dist_attr,
|
||||
DistTensor* out) {
|
||||
if (in.dist_attr().is_partial()) {
|
||||
auto partial_status = in.dist_attr().partial_status();
|
||||
auto out_partial_status = out_dist_attr.partial_status();
|
||||
ReshardContext ctx(dev_ctx, in, out_dist_attr, out);
|
||||
for (const auto& [k, v] : partial_status) {
|
||||
VLOG(3) << "Step1: partial axis " << k;
|
||||
if (out_partial_status.count(k) != 0 || out_dist_attr.is_shard(k)) {
|
||||
continue;
|
||||
}
|
||||
auto strategy = std::make_unique<PartialToReplicate>(-1, k, ctx);
|
||||
strategy->Eval();
|
||||
}
|
||||
VLOG(3) << "After P to R, dist attr is " << out->dist_attr();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessShardToReplicated(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
TensorDistAttr out_dist_attr,
|
||||
DistTensor* out) {
|
||||
auto is_same_shard = [&out_dist_attr, &out](
|
||||
std::vector<int64_t> in_mesh_axis,
|
||||
std::vector<int64_t> out_mesh_axis) {
|
||||
if (in_mesh_axis != out_mesh_axis) {
|
||||
return false;
|
||||
}
|
||||
for (auto dim : in_mesh_axis) {
|
||||
if (out_dist_attr.get_split_factor(dim) !=
|
||||
out->dist_attr().get_split_factor(dim)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
int64_t first_diff_axis =
|
||||
FindFirstDiffShardAxis(out->dist_attr(), out_dist_attr);
|
||||
VLOG(3) << "In S to R, first diff axis is " << first_diff_axis;
|
||||
for (int cur_tensor_dim = first_diff_axis; cur_tensor_dim >= 0;
|
||||
--cur_tensor_dim) {
|
||||
auto in_mesh_axis = out->dist_attr().multi_dims_mapping()[cur_tensor_dim];
|
||||
auto out_mesh_axis = out_dist_attr.multi_dims_mapping()[cur_tensor_dim];
|
||||
if (in_mesh_axis.size() == 0 ||
|
||||
is_same_shard(in_mesh_axis, out_mesh_axis)) {
|
||||
continue;
|
||||
}
|
||||
VLOG(3) << "Step2: in_mesh axis " << auto_parallel::str_join(in_mesh_axis);
|
||||
ReshardContext ctx(dev_ctx, in, out_dist_attr, out);
|
||||
for (int64_t idx = in_mesh_axis.size() - 1; idx >= 0; idx--) {
|
||||
int64_t cur_mesh_dim = in_mesh_axis.at(idx);
|
||||
auto strategy =
|
||||
std::make_unique<ShardToReplicate>(cur_tensor_dim, cur_mesh_dim, ctx);
|
||||
strategy->Eval();
|
||||
}
|
||||
}
|
||||
if (first_diff_axis >= 0) {
|
||||
VLOG(3) << "After S to R, dist attr is " << out->dist_attr();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessReplicatedToPartial(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
TensorDistAttr out_dist_attr,
|
||||
DistTensor* out) {
|
||||
if (out_dist_attr.is_partial()) {
|
||||
const auto& in_partial_status = out->dist_attr().partial_status();
|
||||
const auto& out_partial_status = out_dist_attr.partial_status();
|
||||
for (const auto& [k, v] : out_partial_status) {
|
||||
if (in_partial_status.count(k) != 0) {
|
||||
continue;
|
||||
}
|
||||
VLOG(3) << "Step3: Partial status mesh axis " << k;
|
||||
ReshardContext ctx(dev_ctx, in, out_dist_attr, out);
|
||||
auto strategy = std::make_unique<ReplicateToPartial>(-1, k, ctx);
|
||||
strategy->Eval();
|
||||
}
|
||||
VLOG(3) << "After R to P, dist attr is " << out->dist_attr();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessReplicateOrPartialToShard(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
TensorDistAttr out_dist_attr,
|
||||
DistTensor* out) {
|
||||
int64_t first_diff_axis =
|
||||
FindFirstDiffShardAxis(out->dist_attr(), out_dist_attr);
|
||||
VLOG(3) << "In P or R to S, first diff axis is " << first_diff_axis;
|
||||
for (int64_t cur_tensor_dim = first_diff_axis; cur_tensor_dim >= 0;
|
||||
--cur_tensor_dim) {
|
||||
const auto& in_mesh_axis =
|
||||
out->dist_attr().multi_dims_mapping()[cur_tensor_dim];
|
||||
const auto& out_mesh_axis =
|
||||
out_dist_attr.multi_dims_mapping()[cur_tensor_dim];
|
||||
if (in_mesh_axis == out_mesh_axis) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& in_partial_status = out->dist_attr().partial_status();
|
||||
ReshardContext ctx(dev_ctx, in, out_dist_attr, out);
|
||||
|
||||
for (auto cur_mesh_dim : out_mesh_axis) {
|
||||
bool is_partial = in_partial_status.count(cur_mesh_dim) != 0;
|
||||
VLOG(3) << "Step4: out_mesh axis : " << cur_mesh_dim
|
||||
<< "; partial state :" << is_partial;
|
||||
std::shared_ptr<SameNdMeshReshardFunction::ReshardStrategy> strategy;
|
||||
if (is_partial) {
|
||||
strategy =
|
||||
std::make_unique<PartialToShard>(cur_tensor_dim, cur_mesh_dim, ctx);
|
||||
} else {
|
||||
strategy = std::make_unique<ReplicateToShard>(
|
||||
cur_tensor_dim, cur_mesh_dim, ctx);
|
||||
}
|
||||
strategy->Eval();
|
||||
}
|
||||
}
|
||||
if (first_diff_axis >= 0) {
|
||||
VLOG(3) << "After P or R to S, dist attr is " << out->dist_attr();
|
||||
}
|
||||
}
|
||||
|
||||
bool SameNdMeshReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
RESHARD_SHORTCUT_IF_FALSE(in.dist_attr().process_mesh() ==
|
||||
out_dist_attr.process_mesh());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.process_mesh().ndim() > 1);
|
||||
|
||||
// check the input and output dims_mapping is not equal
|
||||
RESHARD_SHORTCUT_IF_FALSE(in.dist_attr() != out_dist_attr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SameNdMeshReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
auto out_dist_attr_orig = out_dist_attr;
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), in.dist_attr());
|
||||
// 1. change all the partial status to replicated status if needed
|
||||
ProcessPartialToReplicated(dev_ctx, in, out_dist_attr_orig, out);
|
||||
// 2. change all the shard status to replicated status
|
||||
ProcessShardToReplicated(dev_ctx, in, out_dist_attr_orig, out);
|
||||
// 3. Change replicated to partial
|
||||
ProcessReplicatedToPartial(dev_ctx, in, out_dist_attr_orig, out);
|
||||
// 4. Change replicated/partial to shard
|
||||
ProcessReplicateOrPartialToShard(dev_ctx, in, out_dist_attr_orig, out);
|
||||
|
||||
// TODO(lfw): refine this, now is reports wrong info.
|
||||
// Final attr check
|
||||
// PADDLE_ENFORCE_EQ(out->dist_attr() == out_dist_attr_orig,
|
||||
// true,
|
||||
// ::common::errors::InvalidArgument("Expected that out of
|
||||
// reshard has to be target dist, " "out dist att is " +
|
||||
// out->dist_attr().to_string() + ", but target is " +
|
||||
// out_dist_attr_orig.to_string()));
|
||||
}
|
||||
|
||||
bool CrossNdMeshReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const ProcessMesh& in_process_mesh = in.dist_attr().process_mesh();
|
||||
const ProcessMesh& out_process_mesh = out_dist_attr.process_mesh();
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.process_mesh().ndim() > 1);
|
||||
|
||||
// check the input and output dims_mapping is not equal
|
||||
RESHARD_SHORTCUT_IF_FALSE(in.dist_attr() != out_dist_attr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CrossNdMeshReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
// Construct a `DistTensor` by `dtype` of `in` tensor to avoid using default
|
||||
// dtype `float32`. The default dtype `float32` may cause error in amp.
|
||||
DistTensor tmp_result(in.dtype());
|
||||
TensorDistAttr in_dist_attr_shard = in_dist_attr;
|
||||
in_dist_attr_shard.set_partial_status(out_dist_attr.partial_status());
|
||||
in_dist_attr_shard.set_dims_mapping(out_dist_attr.dims_mapping());
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (in_dist_attr.process_mesh().contains(cur_global_rank)) {
|
||||
SameNdMeshReshardFunction same_nd_reshard_func;
|
||||
PADDLE_ENFORCE(
|
||||
same_nd_reshard_func.IsSuitable(in, in_dist_attr_shard),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the same nd reshard function is not valid from %s to %s.",
|
||||
in_dist_attr,
|
||||
in_dist_attr_shard));
|
||||
same_nd_reshard_func.Eval(dev_ctx, in, in_dist_attr_shard, &tmp_result);
|
||||
} else {
|
||||
SetDistProps(&tmp_result, in.dims(), in_dist_attr_shard);
|
||||
}
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
PADDLE_ENFORCE(
|
||||
same_status_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument("Invoke the same status reshard function "
|
||||
"is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
same_status_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API SameNdMeshReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SameNdMeshReshard"; }
|
||||
|
||||
class PADDLE_API ReshardStrategy {
|
||||
public:
|
||||
virtual ~ReshardStrategy() = default;
|
||||
virtual void Eval() = 0;
|
||||
void SetValue(DistTensor* tensor, const DenseTensor& value) {
|
||||
ReshardFunction::SetValue(tensor, value);
|
||||
}
|
||||
|
||||
void SetDistProps(DistTensor* tensor, const TensorDistAttr& dist_attr) {
|
||||
ReshardFunction::SetDistProps(tensor, dist_attr);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class PADDLE_API CrossNdMeshReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "CrossNdMeshReshard"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,149 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/p_to_r_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/all_reduce_kernel.h"
|
||||
#include "paddle/phi/kernels/elementwise_divide_kernel.h"
|
||||
#include "paddle/phi/kernels/full_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool PToRReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
RESHARD_SHORTCUT_IF_FALSE(in.dist_attr().is_partial());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in.dist_attr().process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PToRReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_process_mesh.process_ids();
|
||||
if (in_process_ids.size() == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
const auto& in_partial_status = in_dist_attr.partial_status();
|
||||
auto in_reduce_type = in_partial_status.at(0);
|
||||
bool reduce_mean = false;
|
||||
auto dtype = in.dtype();
|
||||
|
||||
if (in_reduce_type == ReduceType::kRedAvg) {
|
||||
in_reduce_type = ReduceType::kRedSum;
|
||||
reduce_mean = true;
|
||||
}
|
||||
int64_t reduce_type = static_cast<int64_t>(in_reduce_type);
|
||||
VLOG(3) << "Transfer from partial to replicated status with reduce type "
|
||||
<< reduce_type;
|
||||
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
AllReduce,
|
||||
dtype,
|
||||
in_process_ids,
|
||||
in.value(),
|
||||
reduce_type,
|
||||
GetMutableTensor(out));
|
||||
|
||||
if (reduce_mean) {
|
||||
VLOG(3) << "Do reduce mean after all reduce sum";
|
||||
DenseTensor tensor_of_num_process;
|
||||
IntArray shape({1});
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Full,
|
||||
in.dtype(),
|
||||
shape,
|
||||
static_cast<int64_t>(in_process_ids.size()),
|
||||
&tensor_of_num_process);
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Divide,
|
||||
dtype,
|
||||
out->value(),
|
||||
tensor_of_num_process,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool PToRReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_partial());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PToRReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
DistTensor tmp_result;
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
TensorDistAttr tmp_dist_attr = in.dist_attr();
|
||||
tmp_dist_attr.set_process_mesh(out_process_mesh);
|
||||
same_status_func.Eval(dev_ctx, in, tmp_dist_attr, &tmp_result);
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (out_process_mesh.contains(cur_global_rank)) {
|
||||
PToRReshardFunction p_to_r_func;
|
||||
PADDLE_ENFORCE(
|
||||
p_to_r_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the p to r reshard function is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
p_to_r_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
} else {
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
SetValue(out, tmp_result.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API PToRReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
PToRReshardFunction() = default;
|
||||
~PToRReshardFunction() = default;
|
||||
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "PToRReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API PToRReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "PToRReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,238 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/p_to_s_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/concat_kernel.h"
|
||||
#include "paddle/phi/kernels/full_kernel.h"
|
||||
#include "paddle/phi/kernels/reduce_scatter_kernel.h"
|
||||
#include "paddle/phi/kernels/split_kernel.h"
|
||||
#include "paddle/phi/kernels/transpose_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool PToSReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_partial());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReshardPToSWithPadding(DeviceContext* dev_ctx,
|
||||
int64_t split_axis,
|
||||
const std::vector<int64_t>& process_ids,
|
||||
const DenseTensor& in,
|
||||
int64_t padding_nums,
|
||||
DenseTensor* out) {
|
||||
DenseTensor in_reduce_scatter;
|
||||
std::vector<int> axis;
|
||||
const auto& logical_ddim = in.dims();
|
||||
auto dtype = in.dtype();
|
||||
|
||||
if (split_axis != 0) {
|
||||
for (size_t i = 0; i < common::vectorize(logical_ddim).size(); ++i) {
|
||||
axis.emplace_back(i);
|
||||
}
|
||||
std::swap(axis[0], axis[split_axis]);
|
||||
RESHARD_FUNCTOR(dev_ctx, Transpose, dtype, in, axis, &in_reduce_scatter);
|
||||
} else {
|
||||
in_reduce_scatter.ShareDataNoCheckWith(in);
|
||||
}
|
||||
|
||||
DenseTensor out_reduce_scatter;
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
ReduceScatter,
|
||||
dtype,
|
||||
process_ids,
|
||||
in_reduce_scatter,
|
||||
static_cast<int64_t>(process_ids.size()),
|
||||
&out_reduce_scatter);
|
||||
|
||||
DenseTensor out_result;
|
||||
if (split_axis != 0) {
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Transpose, dtype, out_reduce_scatter, axis, &out_result);
|
||||
} else {
|
||||
out_result.ShareDataNoCheckWith(out_reduce_scatter);
|
||||
}
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (cur_global_rank == process_ids.back() && padding_nums != 0) {
|
||||
std::vector<DenseTensor> tmp_out_vec;
|
||||
IntArray tmp_sections(std::vector<int64_t>{
|
||||
out_result.dims()[split_axis] - padding_nums, padding_nums});
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Split,
|
||||
dtype,
|
||||
out_result,
|
||||
tmp_sections,
|
||||
split_axis,
|
||||
&tmp_out_vec);
|
||||
// TODO(liyurui): Since we can not separate local tensor with [0, 10] shape
|
||||
// and uninitialized tensor, here we use a tricky solution.
|
||||
// Give local tensor which has, for example [0, 10] shape, a little
|
||||
// allocation, to make it difference from uninitialized tensor in pipeline
|
||||
// strategy.
|
||||
if (tmp_out_vec[0].dims()[split_axis] == 0) {
|
||||
tmp_out_vec[0].mutable_data(tmp_out_vec[0].place(), 4);
|
||||
}
|
||||
out->ShareDataNoCheckWith(tmp_out_vec[0]);
|
||||
} else {
|
||||
out->ShareDataNoCheckWith(out_result);
|
||||
}
|
||||
}
|
||||
|
||||
void PToSReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_process_mesh.process_ids();
|
||||
|
||||
int out_split_axis =
|
||||
GetSplitAxisWithDimsMapping(out_dist_attr.dims_mapping()).begin()->first;
|
||||
int64_t num_of_process = in_process_mesh.size();
|
||||
if (num_of_process == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
int64_t num_of_padding = in.dims()[out_split_axis] % num_of_process;
|
||||
bool is_balanced_split = (num_of_padding == 0);
|
||||
|
||||
if (is_balanced_split) {
|
||||
VLOG(3) << "Balanced reshard from partial to shard";
|
||||
ReshardPToSWithPadding(dev_ctx,
|
||||
out_split_axis,
|
||||
in_process_ids,
|
||||
in.value(),
|
||||
/*padding_nums*/ 0,
|
||||
GetMutableTensor(out));
|
||||
} else {
|
||||
VLOG(3) << "Unbalanced reshard from partial to shard";
|
||||
int64_t avg_size_on_split_axis =
|
||||
(in.dims()[out_split_axis] + num_of_process - 1) / num_of_process;
|
||||
int64_t padding_nums =
|
||||
avg_size_on_split_axis * num_of_process - in.dims()[out_split_axis];
|
||||
|
||||
DDim concat_local_shape = in.local_dims();
|
||||
concat_local_shape[out_split_axis] = padding_nums;
|
||||
IntArray concat_local_shape_int_array(concat_local_shape.Get(),
|
||||
concat_local_shape.size());
|
||||
auto dtype = in.dtype();
|
||||
|
||||
DenseTensor concat_local_tensor;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Full,
|
||||
dtype,
|
||||
concat_local_shape_int_array,
|
||||
0,
|
||||
&concat_local_tensor);
|
||||
|
||||
DenseTensor in_local_tensor = in.value();
|
||||
std::vector<const DenseTensor*> concat_input_vec = {&in_local_tensor,
|
||||
&concat_local_tensor};
|
||||
|
||||
DenseTensor concat_result;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Concat,
|
||||
dtype,
|
||||
concat_input_vec,
|
||||
out_split_axis,
|
||||
&concat_result);
|
||||
|
||||
ReshardPToSWithPadding(dev_ctx,
|
||||
out_split_axis,
|
||||
in_process_ids,
|
||||
concat_result,
|
||||
padding_nums,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool PToSReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_partial());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PToSReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
DistTensor tmp_result;
|
||||
TensorDistAttr in_dist_attr_shard = in_dist_attr;
|
||||
in_dist_attr_shard.clean_partial_status();
|
||||
in_dist_attr_shard.set_dims_mapping(out_dist_attr.dims_mapping());
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (in_dist_attr.process_mesh().contains(cur_global_rank)) {
|
||||
PToSReshardFunction p_to_s_func;
|
||||
PADDLE_ENFORCE(
|
||||
p_to_s_func.IsSuitable(in, in_dist_attr_shard),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the p to s reshard function is not valid from %s to %s.",
|
||||
in_dist_attr,
|
||||
in_dist_attr_shard));
|
||||
p_to_s_func.Eval(dev_ctx, in, in_dist_attr_shard, &tmp_result);
|
||||
} else {
|
||||
SetDistProps(&tmp_result, in.dims(), in_dist_attr_shard);
|
||||
SetValue(&tmp_result, in.value());
|
||||
}
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
PADDLE_ENFORCE(
|
||||
same_status_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument("Invoke the same status reshard function "
|
||||
"is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
same_status_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API PToSReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "PToSReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API PToSReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "PToSReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/r_to_p_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/assign_kernel.h"
|
||||
#include "paddle/phi/kernels/full_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool RToPReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_partial());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RToPReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_process_ids = in.dist_attr().process_mesh().process_ids();
|
||||
if (in_process_ids.size() == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
int64_t local_rank = GetCurRankCoordInMesh(out_process_mesh)[0];
|
||||
const auto& in_reduce_type = out_dist_attr.partial_status().at(0);
|
||||
|
||||
if (local_rank != 0) {
|
||||
if (in_reduce_type == ReduceType::kRedAvg) {
|
||||
// assign the input value to output
|
||||
RESHARD_FUNCTOR_WITHOUT_DTYPE(
|
||||
dev_ctx, Assign, in.value(), GetMutableTensor(out));
|
||||
} else {
|
||||
// reset the physical tensor to zero
|
||||
IntArray shape(in.local_dims().Get(), in.local_dims().size());
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Full, in.dtype(), shape, 0, GetMutableTensor(out));
|
||||
}
|
||||
} else {
|
||||
// assign the input value to output
|
||||
RESHARD_FUNCTOR_WITHOUT_DTYPE(
|
||||
dev_ctx, Assign, in.value(), GetMutableTensor(out));
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool RToPReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_partial());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RToPReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
DistTensor tmp_result;
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
TensorDistAttr tmp_dist_attr = in.dist_attr();
|
||||
tmp_dist_attr.set_process_mesh(out_process_mesh);
|
||||
same_status_func.Eval(dev_ctx, in, tmp_dist_attr, &tmp_result);
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (out_process_mesh.contains(cur_global_rank)) {
|
||||
RToPReshardFunction r_to_p_func;
|
||||
PADDLE_ENFORCE(
|
||||
r_to_p_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the r to p reshard function is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
r_to_p_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
} else {
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
SetValue(out, tmp_result.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API RToPReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "RToPReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API RToPReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "RToPReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,196 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/r_to_s_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/concat_kernel.h"
|
||||
#include "paddle/phi/kernels/slice_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool RToSReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::map<int, int64_t> GetSplitAxisWithDimsMapping(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping) {
|
||||
std::map<int, int64_t> split_axis_to_mesh_axis;
|
||||
for (size_t i = 0; i < dims_mapping.size(); ++i) {
|
||||
if (dims_mapping[i].size() > 0) {
|
||||
split_axis_to_mesh_axis.emplace(i, dims_mapping[i][0]);
|
||||
}
|
||||
}
|
||||
return split_axis_to_mesh_axis;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void RToSReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_dims_mapping = out_dist_attr.multi_dims_mapping();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
const DenseTensor& in_physical_tensor_cur_rank = in.value();
|
||||
|
||||
std::map<int, int64_t> split_axis_to_mesh_axis =
|
||||
GetSplitAxisWithDimsMapping(out_dims_mapping);
|
||||
std::vector<int64_t> coord_in_mesh = GetCurRankCoordInMesh(out_process_mesh);
|
||||
|
||||
int split_axis = split_axis_to_mesh_axis.begin()->first;
|
||||
int64_t mesh_axis = split_axis_to_mesh_axis.begin()->second;
|
||||
|
||||
VLOG(3) << "split axis is " << split_axis << ", mesh axis is " << mesh_axis;
|
||||
VLOG(3) << "shape size is " << out_process_mesh.shape().size();
|
||||
|
||||
int64_t num_of_process = out_process_mesh.shape()[mesh_axis];
|
||||
if (num_of_process == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
VLOG(3) << "RToSReshard: Tensor will be split on axis " << split_axis
|
||||
<< ". Slice will use axis " << mesh_axis << " of process_mesh."
|
||||
<< " There will have " << num_of_process
|
||||
<< " process participate in.";
|
||||
|
||||
int64_t num_group = out_dist_attr.get_split_factor(mesh_axis);
|
||||
VLOG(3) << "num group = " << num_group;
|
||||
|
||||
std::vector<int64_t> split_num_vec =
|
||||
BalancedSplit(in.value().dims()[split_axis], num_of_process * num_group);
|
||||
|
||||
auto dtype = in_physical_tensor_cur_rank.dtype();
|
||||
|
||||
int64_t slice_stride = num_of_process;
|
||||
std::vector<DenseTensor> dense_out_vec(num_group);
|
||||
for (int64_t i = 0; i < num_group; i++) {
|
||||
int64_t start =
|
||||
split_num_vec[0] * (coord_in_mesh[mesh_axis] + i * slice_stride);
|
||||
int64_t end = std::min(start + split_num_vec[0],
|
||||
in_physical_tensor_cur_rank.dims()[split_axis]);
|
||||
VLOG(4) << "start is " << start << ", end is " << end;
|
||||
|
||||
PADDLE_ENFORCE_LE(start,
|
||||
end,
|
||||
common::errors::InvalidArgument(
|
||||
"Slice Args 'start' should be less or qual to 'end', "
|
||||
"but got 'start' is %d, 'end' is %d.",
|
||||
start,
|
||||
end));
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Slice,
|
||||
dtype,
|
||||
in_physical_tensor_cur_rank,
|
||||
{split_axis},
|
||||
{start},
|
||||
{end},
|
||||
&(dense_out_vec[i]));
|
||||
}
|
||||
|
||||
if (num_group == 1) {
|
||||
SetValue(out, dense_out_vec[0]);
|
||||
} else {
|
||||
std::vector<const DenseTensor*> d_tensor_ptr_vec;
|
||||
for (auto& d_tensor : dense_out_vec) {
|
||||
d_tensor_ptr_vec.push_back(&d_tensor);
|
||||
}
|
||||
|
||||
DenseTensor dense_out;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Concat,
|
||||
dtype,
|
||||
d_tensor_ptr_vec,
|
||||
Scalar(split_axis),
|
||||
&dense_out);
|
||||
SetValue(out, dense_out);
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool RToSReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RToSReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
DistTensor tmp_result;
|
||||
TensorDistAttr in_dist_attr_shard = in_dist_attr;
|
||||
in_dist_attr_shard.set_dims_mapping(out_dist_attr.dims_mapping());
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (in_dist_attr.process_mesh().contains(cur_global_rank)) {
|
||||
RToSReshardFunction r_to_s_func;
|
||||
PADDLE_ENFORCE(
|
||||
r_to_s_func.IsSuitable(in, in_dist_attr_shard),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the r to s reshard function is not valid from %s to %s.",
|
||||
in_dist_attr,
|
||||
in_dist_attr_shard));
|
||||
r_to_s_func.Eval(dev_ctx, in, in_dist_attr_shard, &tmp_result);
|
||||
} else {
|
||||
SetDistProps(&tmp_result, in.dims(), in_dist_attr_shard);
|
||||
SetValue(&tmp_result, in.value());
|
||||
}
|
||||
SameStatusReshardFunction same_status_func;
|
||||
PADDLE_ENFORCE(
|
||||
same_status_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument("Invoke the same status reshard function "
|
||||
"is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
same_status_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API RToSReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "RToSReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API RToSReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "RToSReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,144 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/r_to_x_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/add_n_kernel.h"
|
||||
#include "paddle/phi/kernels/concat_kernel.h"
|
||||
#include "paddle/phi/kernels/elementwise_add_kernel.h"
|
||||
#include "paddle/phi/kernels/full_kernel.h"
|
||||
#include "paddle/phi/kernels/p_recv_kernel.h"
|
||||
#include "paddle/phi/kernels/p_send_kernel.h"
|
||||
#include "paddle/phi/kernels/split_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool RToXExpandReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.process_ids().size() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.process_ids().size() != 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RToXExpandReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& out_dims_mapping = out_dist_attr.dims_mapping();
|
||||
const auto& in_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_mesh = out_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_mesh.process_ids();
|
||||
const auto& out_process_ids = out_mesh.process_ids();
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
int64_t root_rank = in_process_ids[0];
|
||||
auto all_process_ids = GetUnionProcessIds(in_process_ids, out_process_ids);
|
||||
auto dtype = in.dtype();
|
||||
const auto& out_partial_status = out_dist_attr.partial_status();
|
||||
bool cur_rank_in_out_mesh =
|
||||
(std::find(out_process_ids.begin(),
|
||||
out_process_ids.end(),
|
||||
cur_global_rank) != out_process_ids.end());
|
||||
DenseTensor result_value;
|
||||
|
||||
if (root_rank == cur_global_rank) {
|
||||
for (const auto& out_process_id : out_process_ids) {
|
||||
if (out_process_id != root_rank) {
|
||||
// Should acculate the relative rank in communication.
|
||||
auto relative_rank = out_process_id;
|
||||
for (size_t i = 0; i < all_process_ids.size(); ++i) {
|
||||
if (all_process_ids[i] == out_process_id) {
|
||||
relative_rank = i;
|
||||
}
|
||||
}
|
||||
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PSendKernel,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
in.value(),
|
||||
/*peer*/ relative_rank,
|
||||
/*dynamic_shape=*/true);
|
||||
}
|
||||
}
|
||||
if (cur_rank_in_out_mesh) {
|
||||
result_value = in.value();
|
||||
}
|
||||
} else {
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PRecv,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
root_rank,
|
||||
{} /*out_shape*/,
|
||||
/*dynamic_shape=*/true,
|
||||
&result_value);
|
||||
}
|
||||
|
||||
if (cur_rank_in_out_mesh) {
|
||||
if (out_dist_attr.is_partial()) {
|
||||
auto out_reduce_type = out_partial_status.at(0);
|
||||
if (out_reduce_type == ReduceType::kRedSum &&
|
||||
cur_global_rank != out_process_ids[0]) {
|
||||
IntArray shape(result_value.dims().Get(), result_value.dims().size());
|
||||
RESHARD_FUNCTOR(dev_ctx, Full, dtype, shape, 0, &result_value);
|
||||
}
|
||||
SetValue(out, result_value);
|
||||
} else if (out_dist_attr.is_shard()) {
|
||||
std::map<int, int64_t> split_axis_to_mesh_axis =
|
||||
GetSplitAxisWithDimsMapping(out_dims_mapping);
|
||||
std::vector<int64_t> coord_in_mesh = GetCurRankCoordInMesh(out_mesh);
|
||||
|
||||
int split_axis = split_axis_to_mesh_axis.begin()->first;
|
||||
int64_t mesh_axis = split_axis_to_mesh_axis.begin()->second;
|
||||
int64_t num_of_process = out_mesh.shape()[mesh_axis];
|
||||
|
||||
std::vector<int64_t> split_num_vec =
|
||||
BalancedSplit(in.dims()[split_axis], num_of_process);
|
||||
IntArray sections(split_num_vec);
|
||||
|
||||
std::vector<DenseTensor> split_out_vec;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Split,
|
||||
dtype,
|
||||
result_value,
|
||||
sections,
|
||||
split_axis,
|
||||
&split_out_vec);
|
||||
|
||||
SetValue(out, split_out_vec[coord_in_mesh[mesh_axis]]);
|
||||
} else {
|
||||
SetValue(out, result_value);
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class RToXExpandReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "RToXExpandReshard"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/api/profiler/event_tracing.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
using phi::distributed::auto_parallel::str_join;
|
||||
|
||||
std::shared_ptr<DistTensor> ReshardFunction::Eval(
|
||||
DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
phi::RecordEvent reshard_record_event(
|
||||
Name(), phi::TracerEventType::OperatorInner, 1);
|
||||
std::shared_ptr<DistTensor> out = std::make_shared<DistTensor>();
|
||||
Eval(dev_ctx, in, out_dist_attr, out.get());
|
||||
return out;
|
||||
}
|
||||
|
||||
void ReshardFunction::SetValue(DistTensor* tensor, const DenseTensor& value) {
|
||||
tensor->value_ = std::make_shared<DenseTensor>(value);
|
||||
}
|
||||
|
||||
void ReshardFunction::SetDistProps(DistTensor* tensor,
|
||||
const DDim& dims,
|
||||
const TensorDistAttr& dist_attr) {
|
||||
PADDLE_ENFORCE_EQ(dist_attr.verify_dynamic(common::vectorize(dims)),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"The input dist_attr [%s] and dims [%s] are improper.",
|
||||
dist_attr.to_string(),
|
||||
str_join(vectorize(dims))));
|
||||
|
||||
tensor->global_dims_ = dims;
|
||||
tensor->dist_attr_ = dist_attr;
|
||||
tensor->process_mesh_ = dist_attr.process_mesh();
|
||||
tensor->placements_ = ToPlacements(dist_attr);
|
||||
}
|
||||
|
||||
void ReshardFunction::SetDistProps(DistTensor* tensor,
|
||||
const TensorDistAttr& dist_attr) {
|
||||
PADDLE_ENFORCE_EQ(dist_attr.verify_dynamic(common::vectorize(tensor->dims())),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"The input dist_attr [%s] and dims [%s] are improper.",
|
||||
dist_attr.to_string(),
|
||||
str_join(vectorize(tensor->dims()))));
|
||||
|
||||
tensor->dist_attr_ = dist_attr;
|
||||
tensor->process_mesh_ = dist_attr.process_mesh();
|
||||
tensor->placements_ = ToPlacements(dist_attr);
|
||||
}
|
||||
|
||||
DenseTensor* ReshardFunction::GetMutableTensor(DistTensor* tensor) {
|
||||
return tensor->value_.get();
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
|
||||
namespace phi {
|
||||
class DeviceContext;
|
||||
|
||||
namespace distributed {
|
||||
|
||||
class DistTensor;
|
||||
class TensorDistAttr;
|
||||
|
||||
class PADDLE_API ReshardFunction {
|
||||
public:
|
||||
ReshardFunction() = default;
|
||||
virtual ~ReshardFunction() = default;
|
||||
|
||||
virtual bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) = 0;
|
||||
|
||||
std::shared_ptr<DistTensor> Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr);
|
||||
|
||||
virtual void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) = 0;
|
||||
|
||||
virtual std::string Name() { return "ReshardBase"; }
|
||||
|
||||
protected:
|
||||
static void SetValue(DistTensor* tensor, const DenseTensor& value);
|
||||
static void SetDistProps(DistTensor* tensor,
|
||||
const DDim& dims,
|
||||
const TensorDistAttr& dist_attr);
|
||||
static void SetDistProps(DistTensor* tensor, const TensorDistAttr& dist_attr);
|
||||
DenseTensor* GetMutableTensor(DistTensor* tensor);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,81 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/reshard_function_registry.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/global_and_sub_mesh_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/nd_mesh_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/p_to_r_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/p_to_s_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_p_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_s_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_x_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/s_to_p_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/s_to_r_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/s_to_s_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/x_to_r_reshard_function.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
ReshardFunction* ChooseProperReshardFunction(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
for (const auto& func : GetReshardFunctionList()) {
|
||||
if (func->IsSuitable(in, out_dist_attr)) {
|
||||
VLOG(4) << "Choose ReshardFunction: " << func->Name();
|
||||
return func.get();
|
||||
}
|
||||
}
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Can not reshard from in_dist_attr=%s to out_dist_attr=%s.",
|
||||
in.dist_attr().to_string(),
|
||||
out_dist_attr.to_string()));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<ReshardFunction>>& GetReshardFunctionList() {
|
||||
static std::vector<std::unique_ptr<ReshardFunction>> func_list;
|
||||
return func_list;
|
||||
}
|
||||
|
||||
// NOTE: be aware of the registration order of the reshard function.
|
||||
// Higher priority will be granted to the reshard function
|
||||
// which was registered earlier.
|
||||
// Reshard function with higher priority will be evoked
|
||||
// when more than one reshard function satisfy the request.
|
||||
REGISTER_RESHARD_FUNC(SToRReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SToRReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(SToPReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SToPReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(RToSReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(RToSReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(RToPReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(RToPReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(PToRReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(PToRReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(PToSReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(PToSReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(SToSReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SToSReshardFunctionCrossMesh);
|
||||
REGISTER_RESHARD_FUNC(XToRShrinkReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(RToXExpandReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SameStatusReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SameNdMeshReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(CrossNdMeshReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(GlobalToSubMeshReshardFunction);
|
||||
REGISTER_RESHARD_FUNC(SubMeshToGlobalReshardFunction);
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
std::vector<std::unique_ptr<ReshardFunction>>& GetReshardFunctionList();
|
||||
|
||||
#define REGISTER_RESHARD_FUNC(func_type) \
|
||||
class __RegisterReshard_##func_type { \
|
||||
public: \
|
||||
__RegisterReshard_##func_type() { \
|
||||
GetReshardFunctionList().emplace_back(std::make_unique<func_type>()); \
|
||||
} \
|
||||
}; \
|
||||
static __RegisterReshard_##func_type local_reshard_func_##func_type
|
||||
|
||||
ReshardFunction* ChooseProperReshardFunction(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,285 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/backends/device_manager.h"
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/process_mesh.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
namespace {
|
||||
std::string GenUniqueCommKey(const std::vector<int64_t>& process_ids) {
|
||||
std::string unique_comm_key = "ReshardGroup";
|
||||
for (const auto& id : process_ids) {
|
||||
unique_comm_key += "/" + std::to_string(id);
|
||||
}
|
||||
return unique_comm_key;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<int64_t> GetUnionProcessIds(std::vector<int64_t> in_process_ids,
|
||||
std::vector<int64_t> out_process_ids) {
|
||||
std::vector<int64_t> result;
|
||||
std::sort(in_process_ids.begin(), in_process_ids.end());
|
||||
std::sort(out_process_ids.begin(), out_process_ids.end());
|
||||
std::set_union(in_process_ids.begin(),
|
||||
in_process_ids.end(),
|
||||
out_process_ids.begin(),
|
||||
out_process_ids.end(),
|
||||
std::back_inserter(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t GetLocalRankInParticipate(const std::vector<int64_t>& process_ids,
|
||||
int64_t global_rank) {
|
||||
if (global_rank == -1) {
|
||||
global_rank = GetCurGlobalRank();
|
||||
}
|
||||
auto iter = std::find(process_ids.begin(), process_ids.end(), global_rank);
|
||||
PADDLE_ENFORCE_NE(
|
||||
iter,
|
||||
process_ids.end(),
|
||||
common::errors::NotFound(
|
||||
"Global rank %lld cannot be found in process_mesh", global_rank));
|
||||
return iter - process_ids.begin();
|
||||
}
|
||||
|
||||
std::vector<int64_t> GetCurRankCoordInMesh(const ProcessMesh& process_mesh) {
|
||||
const auto& process_shape = process_mesh.shape();
|
||||
const auto& process_ids = process_mesh.process_ids();
|
||||
int64_t ndims_mesh = static_cast<int64_t>(process_shape.size());
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
|
||||
VLOG(3) << "Searching current global rank " << cur_global_rank
|
||||
<< " in process_mesh " << process_mesh;
|
||||
|
||||
auto iter =
|
||||
std::find(process_ids.begin(), process_ids.end(), cur_global_rank);
|
||||
PADDLE_ENFORCE_NE(
|
||||
iter,
|
||||
process_ids.end(),
|
||||
common::errors::NotFound("Rank %lld cannot be found in process_mesh",
|
||||
cur_global_rank));
|
||||
|
||||
int64_t flat_idx_in_mesh = iter - process_ids.begin();
|
||||
|
||||
std::vector<int64_t> coord(ndims_mesh, -1);
|
||||
for (int64_t i = ndims_mesh - 1; i >= 0; --i) {
|
||||
coord[i] = flat_idx_in_mesh % process_shape[i];
|
||||
flat_idx_in_mesh /= process_shape[i];
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
|
||||
CommContext* CreateOrGetCommContext(const DeviceContext& dev_ctx,
|
||||
const std::vector<int64_t>& process_ids) {
|
||||
std::string unique_comm_key = GenUniqueCommKey(process_ids);
|
||||
|
||||
if (!CommContextManager::GetInstance().Has(unique_comm_key)) {
|
||||
int64_t world_size = static_cast<int64_t>(process_ids.size());
|
||||
int64_t rank = GetLocalRankInParticipate(process_ids);
|
||||
VLOG(3) << "local world size: " << world_size << " local rank: " << rank;
|
||||
auto store = CreateOrGetGlobalTCPStore();
|
||||
if (phi::CPUContext::classof(&dev_ctx)) {
|
||||
#if defined(PADDLE_WITH_GLOO)
|
||||
CommContextManager::CreateGlooCommContext(store,
|
||||
unique_comm_key,
|
||||
static_cast<int>(rank),
|
||||
static_cast<int>(world_size));
|
||||
#else
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Cannot use gloo on CPU, please turn PADDLE_WITH_GLOO flag on."));
|
||||
#endif
|
||||
}
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
else if (phi::GPUContext::classof(&dev_ctx)) { // NOLINT
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
CommContextManager::CreateNCCLCommContext(store,
|
||||
unique_comm_key,
|
||||
static_cast<int>(rank),
|
||||
static_cast<int>(world_size));
|
||||
#else
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Cannot use nccl on GPU, please turn WITH_NCCL flag on."));
|
||||
#endif
|
||||
}
|
||||
#elif defined(PADDLE_WITH_XPU)
|
||||
else if (phi::XPUContext::classof(&dev_ctx)) { // NOLINT
|
||||
#if defined(PADDLE_WITH_XPU_BKCL)
|
||||
CommContextManager::CreateBKCLCommContext(store,
|
||||
unique_comm_key,
|
||||
static_cast<int>(rank),
|
||||
static_cast<int>(world_size));
|
||||
#else
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"Cannot use xpu on GPU, please turn WITH_XPU_BKCL flag on."));
|
||||
#endif
|
||||
}
|
||||
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
else if (phi::CustomContext::classof(&dev_ctx)) { // NOLINT
|
||||
CommContextManager::CreateXCCLCommContext(
|
||||
store, unique_comm_key, dev_ctx.GetPlace(), rank, world_size);
|
||||
}
|
||||
#endif
|
||||
else { // NOLINT
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"CommContext is only supported CPU, GPU, XPU, and CustomDevice."));
|
||||
}
|
||||
}
|
||||
|
||||
auto* comm_context = CommContextManager::GetInstance().Get(unique_comm_key);
|
||||
return comm_context;
|
||||
}
|
||||
|
||||
std::map<int, int64_t> GetSplitAxisWithDimsMapping(
|
||||
const std::vector<int64_t>& dims_mapping) {
|
||||
std::map<int, int64_t> split_axis_to_mesh_axis;
|
||||
for (size_t i = 0; i < dims_mapping.size(); ++i) {
|
||||
if (dims_mapping[i] != -1) {
|
||||
split_axis_to_mesh_axis.emplace(i, dims_mapping[i]);
|
||||
}
|
||||
}
|
||||
return split_axis_to_mesh_axis;
|
||||
}
|
||||
|
||||
std::vector<int64_t> BalancedSplit(int64_t total_nums, int64_t num_of_pieces) {
|
||||
bool has_remainder = (total_nums % num_of_pieces != 0);
|
||||
std::vector<int64_t> result(num_of_pieces,
|
||||
(total_nums + num_of_pieces - 1) / num_of_pieces);
|
||||
if (has_remainder) {
|
||||
int64_t& last_value = result.back();
|
||||
last_value = last_value - (last_value * num_of_pieces - total_nums);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsCurRankInMesh(const ProcessMesh& process_mesh) {
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
const auto& process_ids = process_mesh.process_ids();
|
||||
return (std::find(process_ids.begin(), process_ids.end(), cur_global_rank) !=
|
||||
process_ids.end());
|
||||
}
|
||||
|
||||
// Only Input is DistTensor and current device id isn't in DistTensor's mesh
|
||||
// will return true.
|
||||
bool NeedComputationClipForPP(
|
||||
const std::shared_ptr<phi::TensorBase>& tensor_impl) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
DistTensor::classof(tensor_impl.get()),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"The input tensor of NeedComputationClipForPP should be "
|
||||
"``DistTensor``. "
|
||||
"However it's %s",
|
||||
typeid(tensor_impl.get()).name()));
|
||||
return !IsCurRankInMesh(std::static_pointer_cast<DistTensor>(tensor_impl)
|
||||
->dist_attr()
|
||||
.process_mesh());
|
||||
}
|
||||
|
||||
Place GetDefaultPlace() {
|
||||
#if defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
auto dev_types = phi::DeviceManager::GetAllCustomDeviceTypes();
|
||||
if (phi::DeviceManager::GetDeviceCount(dev_types[0]) > 0) {
|
||||
return paddle::DefaultCustomPlace();
|
||||
}
|
||||
#elif defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
if (phi::backends::gpu::GetGPUDeviceCount() >= 0) {
|
||||
return paddle::DefaultGPUPlace();
|
||||
}
|
||||
#elif defined(PADDLE_WITH_XPU)
|
||||
if (phi::backends::xpu::GetXPUDeviceCount() >= 0) {
|
||||
return paddle::DefaultXPUPlace();
|
||||
}
|
||||
#endif
|
||||
return paddle::CPUPlace();
|
||||
}
|
||||
|
||||
DeviceContext* GetDistTensorDeviceContext(DistTensor* input) {
|
||||
// TODO(GhostScreaming): pipeline parallel may create an undefined middle grad
|
||||
// tensor. In such case, we need to get default place.
|
||||
auto place =
|
||||
input && input->initialized() ? input->place() : GetDefaultPlace();
|
||||
return phi::DeviceContextPool::Instance().Get(place);
|
||||
}
|
||||
|
||||
DDim InferShapeForReshardFromReplicate(
|
||||
const std::shared_ptr<DenseTensor>& global_value,
|
||||
const TensorDistAttr& dist_attr) {
|
||||
DDim out_dim = global_value->dims();
|
||||
auto coord_id = GetCurRankCoordInMesh(dist_attr.process_mesh());
|
||||
for (int tensor_axis = 0; tensor_axis < global_value->dims().size();
|
||||
++tensor_axis) {
|
||||
if (dist_attr.is_shard(-1, tensor_axis)) {
|
||||
for (int mesh_axis = 0; mesh_axis < dist_attr.process_mesh().ndim();
|
||||
++mesh_axis) {
|
||||
if (dist_attr.is_shard(mesh_axis, tensor_axis)) {
|
||||
// handle the shard axis
|
||||
int64_t global_shape = out_dim[tensor_axis];
|
||||
int64_t mesh_size = dist_attr.process_mesh().dim_size(mesh_axis);
|
||||
auto balance_shard = BalancedSplit(global_shape, mesh_size);
|
||||
out_dim[tensor_axis] = balance_shard[coord_id[mesh_axis]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out_dim;
|
||||
}
|
||||
|
||||
// 1. Get all the sub meshes of global_mesh
|
||||
// e.g. global_mesh = [[1, 2], [3, 4]], out_mesh = [1, 2] and [3, 4]
|
||||
// global_mesh = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
||||
// out_mesh = [[1, 2], [3, 4]] and [[5, 6], [7, 8]]
|
||||
std::vector<ProcessMesh> GetSubMeshes(const ProcessMesh& process_mesh) {
|
||||
const std::vector<int64_t>& shape = process_mesh.shape();
|
||||
const std::vector<int64_t>& process_ids = process_mesh.process_ids();
|
||||
const std::vector<std::string>& dim_names = process_mesh.dim_names();
|
||||
int64_t total_process_num = process_ids.size();
|
||||
int64_t sub_process_num = total_process_num / shape[0];
|
||||
std::vector<int64_t> sub_process_mesh_shape(shape.begin() + 1, shape.end());
|
||||
std::vector<std::string> sub_process_mesh_dim_names(dim_names.begin() + 1,
|
||||
dim_names.end());
|
||||
|
||||
std::vector<ProcessMesh> sub_process_meshes;
|
||||
for (int i = 0; i < shape[0]; ++i) {
|
||||
int64_t start_position = i * sub_process_num;
|
||||
int64_t end_position = start_position + sub_process_num;
|
||||
std::vector<int64_t> sub_process_ids(process_ids.begin() + start_position,
|
||||
process_ids.begin() + end_position);
|
||||
|
||||
sub_process_meshes.emplace_back(
|
||||
sub_process_mesh_shape, sub_process_ids, sub_process_mesh_dim_names);
|
||||
}
|
||||
return sub_process_meshes;
|
||||
}
|
||||
|
||||
bool IsSubMesh(const ProcessMesh& global_mesh, const ProcessMesh& sub_mesh) {
|
||||
std::vector<ProcessMesh> sub_process_meshes = GetSubMeshes(global_mesh);
|
||||
for (const ProcessMesh& mesh : sub_process_meshes) {
|
||||
if (mesh == sub_mesh) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,231 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/backends/all_context.h"
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/tensor_base.h"
|
||||
#include "paddle/phi/core/visit_type.h"
|
||||
|
||||
#if defined(PADDLE_WITH_XPU)
|
||||
#include <xpu/runtime.h>
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
class DeviceContext;
|
||||
|
||||
namespace distributed {
|
||||
class ProcessMesh;
|
||||
|
||||
PADDLE_API std::vector<int64_t> GetUnionProcessIds(
|
||||
std::vector<int64_t> in_process_ids, std::vector<int64_t> out_process_ids);
|
||||
|
||||
PADDLE_API bool IsCurRankInMesh(const ProcessMesh& process_mesh);
|
||||
|
||||
PADDLE_API bool NeedComputationClipForPP(
|
||||
const std::shared_ptr<phi::TensorBase>& tensor_impl);
|
||||
|
||||
PADDLE_API Place GetDefaultPlace();
|
||||
|
||||
PADDLE_API DeviceContext* GetDistTensorDeviceContext(DistTensor* input);
|
||||
|
||||
PADDLE_API int64_t GetLocalRankInParticipate(
|
||||
const std::vector<int64_t>& process_ids, int64_t global_rank = -1);
|
||||
|
||||
// Get the coordinate of cur rank in process mesh. For example, the process mesh
|
||||
// is [[0, 1], [2, 3], [4, 5], [6, 7]], if the current rank is 4, then will
|
||||
// return [2, 0]; if the current rank is 3, then will return [1, 1].
|
||||
PADDLE_API std::vector<int64_t> GetCurRankCoordInMesh(
|
||||
const ProcessMesh& process_mesh);
|
||||
|
||||
// If the index i's value in dims_mapping is x ( x != -1), means the ith axis of
|
||||
// tensor need be split by xth axis of process_mesh. The function analyze the
|
||||
// input vector, return a key-value map of tensor_split_axis and
|
||||
// process_mesh_split_axis.
|
||||
// For example, if dims_mapping is [-1, 1, -1, 0], will return {1: 1, 3: 0}.
|
||||
PADDLE_API std::map<int, int64_t> GetSplitAxisWithDimsMapping(
|
||||
const std::vector<int64_t>& dims_mapping);
|
||||
|
||||
// If given a number, balance split it to multiple pieces.
|
||||
// For example, the input value is 12, split it to 5 pieces, then return
|
||||
// {3, 3, 2, 2, 2}.
|
||||
PADDLE_API std::vector<int64_t> BalancedSplit(int64_t total_nums,
|
||||
int64_t num_of_pieces);
|
||||
|
||||
// Create a comm context of the input process_ids. Once the newly comm context
|
||||
// created, it will be cached in the global instance, and get from the global
|
||||
// cache later. If the input dev_ctx is GPU, then nccl comm context will be
|
||||
// created. If the input dev_ctx is CPU, then gloo comm context will be created.
|
||||
PADDLE_API CommContext* CreateOrGetCommContext(
|
||||
const DeviceContext& dev_ctx, const std::vector<int64_t>& process_ids);
|
||||
|
||||
PADDLE_API DDim InferShapeForReshardFromReplicate(
|
||||
const std::shared_ptr<DenseTensor>& global_value,
|
||||
const TensorDistAttr& dist_attr);
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
#define DEVICE_CONTEXT GPUContext
|
||||
#elif defined(PADDLE_WITH_XPU)
|
||||
#define DEVICE_CONTEXT XPUContext
|
||||
#elif defined(PADDLE_WITH_CUSTOM_DEVICE)
|
||||
#define DEVICE_CONTEXT CustomContext
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_XPU)
|
||||
#define DEVICE_WAIT(dev_ctx) \
|
||||
do { \
|
||||
xpu_wait(); \
|
||||
(dev_ctx)->Wait(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEVICE_WAIT(dev_ctx) \
|
||||
do { \
|
||||
} while (0) // no need to wait on other devices.
|
||||
#endif
|
||||
|
||||
// Some reshard function supports fewer data types on xpu than on gpu. For
|
||||
// example, `Transpose`, `Split`, and `Divide` do not support double type.
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
|
||||
#define PD_VISIT_RESHARD_TYPES PD_VISIT_BOOL_AND_FLOATING_AND_INTEGRAL_TYPES
|
||||
#else
|
||||
#define PD_VISIT_RESHARD_TYPES(TYPE, NAME, ...) \
|
||||
[&] { \
|
||||
const auto& __dtype__ = TYPE; \
|
||||
switch (__dtype__) { \
|
||||
PD_PRIVATE_CASE_TYPE(NAME, ::phi::DataType::INT32, int, __VA_ARGS__) \
|
||||
PD_PRIVATE_CASE_TYPE(NAME, ::phi::DataType::INT64, int64_t, __VA_ARGS__) \
|
||||
PD_PRIVATE_CASE_TYPE(NAME, ::phi::DataType::FLOAT32, float, __VA_ARGS__) \
|
||||
PD_PRIVATE_CASE_TYPE( \
|
||||
NAME, ::phi::DataType::FLOAT16, ::phi::float16, __VA_ARGS__) \
|
||||
PD_PRIVATE_CASE_TYPE_BFLOAT16(NAME, __VA_ARGS__) \
|
||||
default: \
|
||||
PD_THROW("Reshard function " #NAME \
|
||||
" is not implemented" \
|
||||
" for data type `", \
|
||||
__dtype__, \
|
||||
"`"); \
|
||||
} \
|
||||
}()
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_XPU)
|
||||
#define RESHARD_FUNCTOR_IMPL(dev_ctx, fn_name, dtype, ...) \
|
||||
do { \
|
||||
if (phi::CPUContext::classof(dev_ctx)) { \
|
||||
VLOG(4) << "Call `" << #fn_name << "` in Resharding on CPU."; \
|
||||
PD_VISIT_BOOL_AND_FLOATING_AND_INTEGRAL_TYPES_CPU( \
|
||||
dtype, #fn_name, ([&] { \
|
||||
fn_name<data_t>(static_cast<const CPUContext&>(*dev_ctx), \
|
||||
__VA_ARGS__); \
|
||||
})); \
|
||||
} else if (DEVICE_CONTEXT::classof(dev_ctx)) { \
|
||||
DEVICE_WAIT(dev_ctx); \
|
||||
VLOG(4) << "Call `" << #fn_name << "` in Resharding on device."; \
|
||||
PD_VISIT_RESHARD_TYPES( \
|
||||
dtype, #fn_name, ([&] { \
|
||||
fn_name<data_t>(static_cast<const DEVICE_CONTEXT&>(*dev_ctx), \
|
||||
__VA_ARGS__); \
|
||||
})); \
|
||||
DEVICE_WAIT(dev_ctx); \
|
||||
} else { \
|
||||
PADDLE_THROW(common::errors::Unimplemented( \
|
||||
"The %s in reshard only supported on CPU, GPU, and XPU for now.", \
|
||||
#fn_name)); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define RESHARD_FUNCTOR_IMPL(dev_ctx, fn_name, dtype, ...) \
|
||||
do { \
|
||||
if (phi::CPUContext::classof(dev_ctx)) { \
|
||||
VLOG(4) << "Call `" << #fn_name << "` in Resharding on CPU."; \
|
||||
PD_VISIT_BOOL_AND_FLOATING_AND_INTEGRAL_TYPES_CPU( \
|
||||
dtype, #fn_name, ([&] { \
|
||||
fn_name<data_t>(static_cast<const CPUContext&>(*dev_ctx), \
|
||||
__VA_ARGS__); \
|
||||
})); \
|
||||
} else { \
|
||||
PADDLE_THROW(common::errors::Unimplemented( \
|
||||
"The %s in reshard only supported on CPU for now.", #fn_name)); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define RESHARD_FUNCTOR_WITH_COMM(dev_ctx, fn_name, dtype, process_ids, ...) \
|
||||
do { \
|
||||
auto* comm_context = CreateOrGetCommContext(*dev_ctx, process_ids); \
|
||||
dev_ctx->SetCommContext(comm_context); \
|
||||
RESHARD_FUNCTOR_IMPL(dev_ctx, fn_name, dtype, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define RESHARD_FUNCTOR(dev_ctx, fn_name, dtype, ...) \
|
||||
do { \
|
||||
RESHARD_FUNCTOR_IMPL(dev_ctx, fn_name, dtype, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) || \
|
||||
defined(PADDLE_WITH_XPU)
|
||||
#define RESHARD_FUNCTOR_WITHOUT_DTYPE(dev_ctx, fn_name, ...) \
|
||||
do { \
|
||||
if (phi::CPUContext::classof(dev_ctx)) { \
|
||||
VLOG(4) << "Call `" << #fn_name \
|
||||
<< "`without DType in Resharding on CPU."; \
|
||||
fn_name(static_cast<const CPUContext&>(*dev_ctx), __VA_ARGS__); \
|
||||
} else if (DEVICE_CONTEXT::classof(dev_ctx)) { \
|
||||
VLOG(4) << "Call `" << #fn_name \
|
||||
<< "`without DType in Resharding on device."; \
|
||||
fn_name(static_cast<const DEVICE_CONTEXT&>(*dev_ctx), __VA_ARGS__); \
|
||||
} else { \
|
||||
PADDLE_THROW(common::errors::Unimplemented( \
|
||||
"The %s in reshard only supported CPU, GPU, and XPU Device", \
|
||||
#fn_name)); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define RESHARD_FUNCTOR_WITHOUT_DTYPE(dev_ctx, fn_name, ...) \
|
||||
do { \
|
||||
if (phi::CPUContext::classof(dev_ctx)) { \
|
||||
VLOG(4) << "Call `" << #fn_name \
|
||||
<< "`without DType in Resharding on CPU."; \
|
||||
fn_name(static_cast<const CPUContext&>(*dev_ctx), __VA_ARGS__); \
|
||||
} else { \
|
||||
PADDLE_THROW(common::errors::Unimplemented( \
|
||||
"The %s in reshard only supported CPU, GPU, and XPU Device.", \
|
||||
#fn_name)); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define RESHARD_SHORTCUT_IF_FALSE(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
std::vector<ProcessMesh> GetSubMeshes(const ProcessMesh& process_mesh);
|
||||
PADDLE_API bool IsSubMesh(const ProcessMesh& global_mesh,
|
||||
const ProcessMesh& sub_mesh);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/s_to_p_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/r_to_p_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/s_to_r_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/reduce_scatter_kernel.h"
|
||||
#include "paddle/phi/kernels/transpose_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool SToPReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_partial());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToPReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
|
||||
// step 1, create tmp dist attr and tmp dist tensor
|
||||
TensorDistAttr tmp_attr(out_dist_attr);
|
||||
DistTensor tmp_tensor;
|
||||
tmp_attr.clean_partial_status();
|
||||
|
||||
// step 2, do s to r reshard on `in` to `tmp`
|
||||
SToRReshardFunction s_to_r;
|
||||
s_to_r.Eval(dev_ctx, in, tmp_attr, &tmp_tensor);
|
||||
|
||||
// step 3, do r to p reshard on `tmp` to `out`
|
||||
RToPReshardFunction r_to_p;
|
||||
r_to_p.Eval(dev_ctx, tmp_tensor, out_dist_attr, out);
|
||||
}
|
||||
|
||||
bool SToPReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_partial());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToPReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
DistTensor tmp_result;
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
TensorDistAttr tmp_dist_attr = in.dist_attr();
|
||||
tmp_dist_attr.set_process_mesh(out_process_mesh);
|
||||
same_status_func.Eval(dev_ctx, in, tmp_dist_attr, &tmp_result);
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (out_process_mesh.contains(cur_global_rank)) {
|
||||
SToPReshardFunction s_to_p_func;
|
||||
PADDLE_ENFORCE(
|
||||
s_to_p_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the s to p reshard function is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
s_to_p_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
} else {
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
SetValue(out, tmp_result.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API SToPReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToPReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API SToPReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToPReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,257 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/s_to_r_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/common/int_array.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/all_gather_kernel.h"
|
||||
#include "paddle/phi/kernels/concat_kernel.h"
|
||||
#include "paddle/phi/kernels/full_kernel.h"
|
||||
#include "paddle/phi/kernels/split_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
namespace {
|
||||
|
||||
void ReshardSToRWithPadding(DeviceContext* dev_ctx,
|
||||
int64_t split_axis,
|
||||
const std::vector<int64_t>& process_ids,
|
||||
const DenseTensor& in,
|
||||
int64_t padding_nums,
|
||||
DenseTensor* out,
|
||||
int64_t split_factor = 1) {
|
||||
VLOG(4) << "split factor is " << split_factor;
|
||||
int64_t num_of_process = process_ids.size();
|
||||
auto dtype = in.dtype();
|
||||
|
||||
// For balanced split to replicate, we need to do all gather first.
|
||||
// If the input value doesn't split on axis 0, we need to split
|
||||
// and concat on specific axis.
|
||||
RESHARD_FUNCTOR_WITH_COMM(
|
||||
dev_ctx, AllGather, dtype, process_ids, in, num_of_process, out);
|
||||
|
||||
if (split_axis != 0 || padding_nums != 0 || split_factor > 1) {
|
||||
PADDLE_ENFORCE_EQ((in.dims()[0] % split_factor),
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"The specified axis of tensor should be evenly "
|
||||
"splited, but got %d and %d.",
|
||||
in.dims()[0],
|
||||
split_factor));
|
||||
IntArray sections(std::vector<int64_t>(num_of_process * split_factor,
|
||||
in.dims()[0] / split_factor));
|
||||
|
||||
std::vector<DenseTensor> split_out_vec;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Split,
|
||||
dtype,
|
||||
*out,
|
||||
sections,
|
||||
/*split_axis*/ 0,
|
||||
&split_out_vec);
|
||||
|
||||
if (padding_nums != 0) {
|
||||
std::vector<DenseTensor> tmp_out_vec;
|
||||
IntArray tmp_sections(std::vector<int64_t>{
|
||||
in.dims()[split_axis] - padding_nums, padding_nums});
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Split,
|
||||
dtype,
|
||||
split_out_vec[num_of_process - 1],
|
||||
tmp_sections,
|
||||
split_axis,
|
||||
&tmp_out_vec);
|
||||
split_out_vec[num_of_process - 1] = tmp_out_vec[0];
|
||||
}
|
||||
|
||||
// Concat the result after split on correct axis.
|
||||
std::vector<const DenseTensor*> concat_input_vec;
|
||||
concat_input_vec.reserve(split_out_vec.size());
|
||||
|
||||
for (size_t idx = 0; idx < split_out_vec.size(); idx++) {
|
||||
int64_t new_idx =
|
||||
idx % num_of_process * split_factor + idx / num_of_process;
|
||||
concat_input_vec.emplace_back(&(split_out_vec.at(new_idx)));
|
||||
}
|
||||
|
||||
RESHARD_FUNCTOR(dev_ctx, Concat, dtype, concat_input_vec, split_axis, out);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, int64_t> GetSplitAxisWithDimsMapping(
|
||||
const std::vector<std::vector<int64_t>>& dims_mapping) {
|
||||
std::map<int, int64_t> split_axis_to_mesh_axis;
|
||||
for (size_t i = 0; i < dims_mapping.size(); ++i) {
|
||||
if (dims_mapping[i].size() > 0) {
|
||||
split_axis_to_mesh_axis.emplace(i, dims_mapping[i][0]);
|
||||
}
|
||||
}
|
||||
return split_axis_to_mesh_axis;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool SToRReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToRReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& in_dims_mapping = in_dist_attr.multi_dims_mapping();
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_process_mesh.process_ids();
|
||||
if (in_process_ids.size() == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
|
||||
int split_axis = GetSplitAxisWithDimsMapping(in_dims_mapping).begin()->first;
|
||||
int64_t mesh_axis =
|
||||
GetSplitAxisWithDimsMapping(in_dims_mapping).begin()->second;
|
||||
int64_t num_of_process = in_process_mesh.size();
|
||||
int64_t num_of_padding = in.dims()[split_axis] % num_of_process;
|
||||
VLOG(4) << "dist attr " << in_dist_attr.to_string();
|
||||
int64_t split_factor = in_dist_attr.get_split_factor(mesh_axis);
|
||||
bool is_balanced_split = (num_of_padding == 0);
|
||||
|
||||
if (is_balanced_split) {
|
||||
VLOG(3) << "Balanced reshard from shard to replicated";
|
||||
ReshardSToRWithPadding(dev_ctx,
|
||||
split_axis,
|
||||
in_process_ids,
|
||||
in.value(),
|
||||
/*padding_nums*/ 0,
|
||||
GetMutableTensor(out),
|
||||
split_factor);
|
||||
} else {
|
||||
VLOG(3) << "Unbalanced reshard from shard to replicated";
|
||||
int64_t avg_size_on_split_axis =
|
||||
(in.dims()[split_axis] + num_of_process - 1) / num_of_process;
|
||||
int64_t padding_nums =
|
||||
avg_size_on_split_axis * num_of_process - in.dims()[split_axis];
|
||||
bool need_padding = (in.local_dims()[split_axis] != avg_size_on_split_axis);
|
||||
|
||||
if (need_padding) {
|
||||
DDim concat_local_shape = in.local_dims();
|
||||
concat_local_shape[split_axis] = padding_nums;
|
||||
IntArray concat_local_shape_int_array(concat_local_shape.Get(),
|
||||
concat_local_shape.size());
|
||||
auto dtype = in.dtype();
|
||||
|
||||
DenseTensor concat_local_tensor;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Full,
|
||||
dtype,
|
||||
concat_local_shape_int_array,
|
||||
0,
|
||||
&concat_local_tensor);
|
||||
|
||||
DenseTensor in_local_tensor = in.value();
|
||||
std::vector<const DenseTensor*> concat_input_vec = {&in_local_tensor,
|
||||
&concat_local_tensor};
|
||||
|
||||
DenseTensor concat_result;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Concat, dtype, concat_input_vec, split_axis, &concat_result);
|
||||
ReshardSToRWithPadding(dev_ctx,
|
||||
split_axis,
|
||||
in_process_ids,
|
||||
concat_result,
|
||||
padding_nums,
|
||||
GetMutableTensor(out));
|
||||
} else {
|
||||
ReshardSToRWithPadding(dev_ctx,
|
||||
split_axis,
|
||||
in_process_ids,
|
||||
in.value(),
|
||||
padding_nums,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool SToRReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToRReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
DistTensor tmp_result;
|
||||
|
||||
TensorDistAttr tmp_dist_attr = in.dist_attr();
|
||||
tmp_dist_attr.set_process_mesh(out_process_mesh);
|
||||
same_status_func.Eval(dev_ctx, in, tmp_dist_attr, &tmp_result);
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (out_process_mesh.contains(cur_global_rank)) {
|
||||
SToRReshardFunction s_to_r_func;
|
||||
PADDLE_ENFORCE(
|
||||
s_to_r_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the s to r reshard function is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
s_to_r_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
} else {
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
SetValue(out, tmp_result.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API SToRReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
SToRReshardFunction() = default;
|
||||
~SToRReshardFunction() = default;
|
||||
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToRReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API SToRReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToRReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,198 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/s_to_s_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/all_to_all_kernel.h"
|
||||
#include "paddle/phi/kernels/reshape_kernel.h"
|
||||
#include "paddle/phi/kernels/transpose_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool SToSReshardFunction::IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.multi_dims_mapping() !=
|
||||
out_dist_attr.multi_dims_mapping());
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh == out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToSReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_process_mesh = in.dist_attr().process_mesh();
|
||||
const auto& in_process_ids = in_process_mesh.process_ids();
|
||||
if (in_process_ids.size() == 1) {
|
||||
SetValue(out, in.value());
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
return;
|
||||
}
|
||||
auto dtype = in.dtype();
|
||||
const auto& logical_ddim = in.dims();
|
||||
int64_t nranks = static_cast<int64_t>(in_process_ids.size());
|
||||
int in_split_axis =
|
||||
GetSplitAxisWithDimsMapping(in.dist_attr().dims_mapping()).begin()->first;
|
||||
int out_split_axis =
|
||||
GetSplitAxisWithDimsMapping(out_dist_attr.dims_mapping()).begin()->first;
|
||||
|
||||
DenseTensor in_all_to_all;
|
||||
// 1. preprocess, reshape and transpose the input tensor
|
||||
if (out_split_axis != 0) {
|
||||
// 1.1 calc the shape and reshape
|
||||
std::vector<int64_t> pre_shape_vec = common::vectorize(logical_ddim);
|
||||
pre_shape_vec[in_split_axis] /= nranks;
|
||||
pre_shape_vec[out_split_axis] /= nranks;
|
||||
pre_shape_vec.insert(pre_shape_vec.begin() + out_split_axis, nranks);
|
||||
|
||||
DenseTensor out_reshape1;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Reshape, dtype, in.value(), pre_shape_vec, &out_reshape1);
|
||||
|
||||
// 1.2 calc the the desire axis and transpose
|
||||
std::vector<int> axis;
|
||||
axis.emplace_back(out_split_axis);
|
||||
for (size_t i = 0; i < pre_shape_vec.size(); ++i) {
|
||||
if (static_cast<int>(i) != out_split_axis) {
|
||||
axis.emplace_back(i);
|
||||
}
|
||||
}
|
||||
DenseTensor out_transpose;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Transpose, dtype, out_reshape1, axis, &out_transpose);
|
||||
|
||||
// 1.3 calc the final shape and reshape
|
||||
pre_shape_vec.erase(pre_shape_vec.begin() + out_split_axis);
|
||||
pre_shape_vec[in_split_axis] *= nranks;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Reshape, dtype, out_transpose, pre_shape_vec, &in_all_to_all);
|
||||
} else {
|
||||
in_all_to_all.ShareDataNoCheckWith(in.value());
|
||||
}
|
||||
|
||||
// 2. use all to all to switch data to other ranks
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
AllToAll,
|
||||
dtype,
|
||||
in_process_ids,
|
||||
in_all_to_all,
|
||||
GetMutableTensor(out));
|
||||
|
||||
// 3. postprocess, reshape and transpose the output tensor
|
||||
if (in_split_axis != 0) {
|
||||
// 3.1 calc the shape and reshape
|
||||
std::vector<int64_t> post_shape_vec = common::vectorize(logical_ddim);
|
||||
post_shape_vec[in_split_axis] /= nranks;
|
||||
post_shape_vec[out_split_axis] /= nranks;
|
||||
post_shape_vec.insert(post_shape_vec.begin(), nranks);
|
||||
|
||||
DenseTensor out_reshape1;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Reshape, dtype, out->value(), post_shape_vec, &out_reshape1);
|
||||
|
||||
// 3.2 calc the the desire axis and transpose
|
||||
std::vector<int> axis;
|
||||
for (size_t i = 1; i < post_shape_vec.size(); ++i) {
|
||||
axis.emplace_back(i);
|
||||
}
|
||||
axis.insert(axis.begin() + in_split_axis, 0);
|
||||
DenseTensor out_transpose;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Transpose, dtype, out_reshape1, axis, &out_transpose);
|
||||
|
||||
// 3.3 calc the final shape and reshape
|
||||
post_shape_vec.erase(post_shape_vec.begin());
|
||||
post_shape_vec[in_split_axis] *= nranks;
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Reshape,
|
||||
dtype,
|
||||
out_transpose,
|
||||
post_shape_vec,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
bool SToSReshardFunctionCrossMesh::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.multi_dims_mapping() !=
|
||||
out_dist_attr.multi_dims_mapping());
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.is_shard());
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_shard());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SToSReshardFunctionCrossMesh::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
SameStatusReshardFunction same_status_func;
|
||||
DistTensor tmp_result;
|
||||
|
||||
TensorDistAttr tmp_dist_attr = in.dist_attr();
|
||||
tmp_dist_attr.set_process_mesh(out_process_mesh);
|
||||
same_status_func.Eval(dev_ctx, in, tmp_dist_attr, &tmp_result);
|
||||
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
if (out_process_mesh.contains(cur_global_rank)) {
|
||||
SToSReshardFunction s_to_s_func;
|
||||
PADDLE_ENFORCE(
|
||||
s_to_s_func.IsSuitable(tmp_result, out_dist_attr),
|
||||
common::errors::InvalidArgument(
|
||||
"Invoke the s to s reshard function is not valid from %s to %s.",
|
||||
tmp_result.dist_attr(),
|
||||
out_dist_attr));
|
||||
s_to_s_func.Eval(dev_ctx, tmp_result, out_dist_attr, out);
|
||||
} else {
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
SetValue(out, tmp_result.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API SToSReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
SToSReshardFunction() = default;
|
||||
~SToSReshardFunction() = default;
|
||||
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToSReshard"; }
|
||||
};
|
||||
|
||||
class PADDLE_API SToSReshardFunctionCrossMesh final : public ReshardFunction {
|
||||
public:
|
||||
SToSReshardFunctionCrossMesh() = default;
|
||||
~SToSReshardFunctionCrossMesh() = default;
|
||||
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SToSReshardCrossMesh"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/same_status_reshard_function.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/p_recv_kernel.h"
|
||||
#include "paddle/phi/kernels/p_send_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool SameStatusReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.multi_dims_mapping() ==
|
||||
out_dist_attr.multi_dims_mapping());
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_dist_attr.partial_dims() ==
|
||||
out_dist_attr.partial_dims());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh != out_process_mesh);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.shape() ==
|
||||
out_process_mesh.shape());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SameStatusReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_process_mesh.process_ids();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
const auto& out_process_ids = out_process_mesh.process_ids();
|
||||
auto all_process_ids = GetUnionProcessIds(in_process_ids, out_process_ids);
|
||||
|
||||
// TODO(GhostScreaming): After cross-mesh reshard, current device may
|
||||
// needs to execute next layer. When it construct next layer's backward
|
||||
// graph, out->place() will be called such as in SetGradOutMeta method. As
|
||||
// a result, out can't be undefined. Try to allocate a zero-memory value
|
||||
// for out. Following send/recv will cover this empty DenseTensor
|
||||
// construction.
|
||||
VLOG(3) << "Same_status_reshard_function create an empty DenseTensor for "
|
||||
"cross-mesh DistTensor.";
|
||||
*(out->unsafe_mutable_value()) =
|
||||
DenseTensor(std::make_shared<phi::Allocation>(
|
||||
nullptr, 0, phi::distributed::GetDefaultPlace()),
|
||||
in.value().meta());
|
||||
|
||||
std::vector<std::pair<int64_t, int64_t>> p2p_pair;
|
||||
for (size_t i = 0; i < out_process_ids.size(); ++i) {
|
||||
p2p_pair.emplace_back(in_process_ids[i], out_process_ids[i]);
|
||||
}
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
for (const auto& iter : p2p_pair) {
|
||||
int64_t src = iter.first;
|
||||
int64_t dst = iter.second;
|
||||
if (src == cur_global_rank) {
|
||||
VLOG(3) << "Send from src " << src << " to dst " << dst;
|
||||
int64_t dst_local_rank = GetLocalRankInParticipate(all_process_ids, dst);
|
||||
// Since send kernel only has input, so we don't need to infermeta
|
||||
// actually. According to this reason, just use the kernel directly.
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PSendKernel,
|
||||
in.dtype(),
|
||||
all_process_ids,
|
||||
in.value(),
|
||||
dst_local_rank,
|
||||
/*dynamic_shape=*/true);
|
||||
// TODO(liyurui): Use dynamic shape will lead to poor performance, but we
|
||||
// don't have any other good idea now. For the following reasons:
|
||||
// 1. We can not ensure the meta being right deduce by the infermeta.
|
||||
// 2. The meta of some kernels can't decide in compile time.
|
||||
// 3. DenseTensor with empty value only need infermeta and skip the real
|
||||
// kernel execution.
|
||||
} else if (dst == cur_global_rank) {
|
||||
VLOG(3) << "Recv from src " << src << " to dst " << dst;
|
||||
int64_t src_local_rank = GetLocalRankInParticipate(all_process_ids, src);
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PRecv,
|
||||
in.dtype(),
|
||||
all_process_ids,
|
||||
src_local_rank,
|
||||
{} /*out_shape*/,
|
||||
/*dynamic_shape=*/true,
|
||||
GetMutableTensor(out));
|
||||
}
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API SameStatusReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "SameStatusReshard"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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/core/distributed/auto_parallel/reshard/x_to_r_reshard_function.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/dist_tensor.h"
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/store_utils.h"
|
||||
#include "paddle/phi/kernels/add_n_kernel.h"
|
||||
#include "paddle/phi/kernels/concat_kernel.h"
|
||||
#include "paddle/phi/kernels/elementwise_add_kernel.h"
|
||||
#include "paddle/phi/kernels/p_recv_kernel.h"
|
||||
#include "paddle/phi/kernels/p_send_kernel.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool XToRShrinkReshardFunction::IsSuitable(
|
||||
const DistTensor& in, const TensorDistAttr& out_dist_attr) {
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_dist_attr.is_replicated());
|
||||
|
||||
const auto& in_process_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_process_mesh = out_dist_attr.process_mesh();
|
||||
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.ndim() == 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(in_process_mesh.process_ids().size() != 1);
|
||||
RESHARD_SHORTCUT_IF_FALSE(out_process_mesh.process_ids().size() == 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XToRShrinkReshardFunction::Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) {
|
||||
VLOG(3) << "Call " << Name();
|
||||
const auto& in_dist_attr = in.dist_attr();
|
||||
const auto& in_dims_mapping = in_dist_attr.dims_mapping();
|
||||
const auto& in_mesh = in_dist_attr.process_mesh();
|
||||
const auto& out_mesh = out_dist_attr.process_mesh();
|
||||
const auto& in_process_ids = in_mesh.process_ids();
|
||||
const auto& out_process_ids = out_mesh.process_ids();
|
||||
int64_t cur_global_rank = GetCurGlobalRank();
|
||||
int64_t root_rank = out_process_ids[0];
|
||||
auto dtype = in.dtype();
|
||||
const auto& in_partial_status = in_dist_attr.partial_status();
|
||||
auto all_process_ids = GetUnionProcessIds(in_process_ids, out_process_ids);
|
||||
std::unordered_map<int64_t, DenseTensor> rank_to_result;
|
||||
|
||||
// Step 1: other ranks need to send value to the root
|
||||
if (!in_dist_attr.is_replicated()) {
|
||||
if (cur_global_rank != root_rank) {
|
||||
// send dense tensor to root
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PSendKernel,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
in.value(),
|
||||
root_rank,
|
||||
/*dynamic_shape=*/true);
|
||||
} else {
|
||||
for (size_t i = 0; i < all_process_ids.size(); ++i) {
|
||||
if (all_process_ids[i] != root_rank) {
|
||||
rank_to_result.emplace(all_process_ids[i], DenseTensor());
|
||||
RESHARD_FUNCTOR_WITH_COMM(dev_ctx,
|
||||
PRecv,
|
||||
dtype,
|
||||
all_process_ids,
|
||||
all_process_ids[i],
|
||||
{} /*out_shape*/,
|
||||
/*dynamic_shape=*/true,
|
||||
&rank_to_result[all_process_ids[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: concat or reduce based on dist attr
|
||||
if (cur_global_rank == root_rank) {
|
||||
std::vector<const DenseTensor*> input_vec;
|
||||
for (const auto& in_process_id : in_process_ids) {
|
||||
if (in_process_id == cur_global_rank) {
|
||||
input_vec.emplace_back(&(in.value()));
|
||||
} else {
|
||||
input_vec.emplace_back(&(rank_to_result[in_process_id]));
|
||||
}
|
||||
}
|
||||
if (in_dist_attr.is_shard()) {
|
||||
int split_axis =
|
||||
GetSplitAxisWithDimsMapping(in_dims_mapping).begin()->first;
|
||||
RESHARD_FUNCTOR(
|
||||
dev_ctx, Concat, dtype, input_vec, split_axis, GetMutableTensor(out));
|
||||
} else if (in_dist_attr.is_partial()) {
|
||||
auto in_reduce_type = in_partial_status.at(0);
|
||||
if (in_reduce_type == ReduceType::kRedSum) {
|
||||
DenseTensor result_add_out = *input_vec[0];
|
||||
for (size_t i = 1; i < input_vec.size(); ++i) {
|
||||
RESHARD_FUNCTOR(dev_ctx,
|
||||
Add,
|
||||
dtype,
|
||||
*input_vec[i],
|
||||
result_add_out,
|
||||
&result_add_out);
|
||||
}
|
||||
SetValue(out, result_add_out);
|
||||
} else {
|
||||
PADDLE_THROW(common::errors::Unavailable(
|
||||
"The reduce type is not supported, will be supported soon."));
|
||||
}
|
||||
} else {
|
||||
SetValue(out, in.value());
|
||||
}
|
||||
SetDistProps(out, in.dims(), out_dist_attr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/reshard/reshard_function.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API XToRShrinkReshardFunction final : public ReshardFunction {
|
||||
public:
|
||||
bool IsSuitable(const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr) override;
|
||||
|
||||
void Eval(DeviceContext* dev_ctx,
|
||||
const DistTensor& in,
|
||||
const TensorDistAttr& out_dist_attr,
|
||||
DistTensor* out) override;
|
||||
|
||||
std::string Name() override { return "XToRShrinkReshard"; }
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,191 @@
|
||||
/* 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. */
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
namespace auto_parallel {
|
||||
|
||||
// struct Indent {
|
||||
// Indent(int &level) : level(level) { ++level; }
|
||||
// ~Indent() { --level; }
|
||||
// int &level;
|
||||
// };
|
||||
|
||||
// inline std::string str_indent(std::string& str, cur_indent) {
|
||||
// string spaces(cur_indent, " ");
|
||||
// return str + std::string(cur_indent, " ");
|
||||
// }
|
||||
|
||||
template <class T>
|
||||
bool has_duplicates(const std::vector<T>& vec) {
|
||||
std::unordered_map<T, int> map;
|
||||
for (const auto& i : vec) {
|
||||
++map[i];
|
||||
if (map[i] > 1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int64_t canonical_dim(int dim, int ndim) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
dim >= -ndim && dim < ndim,
|
||||
true,
|
||||
errors::InvalidArgument(
|
||||
"Dimension %d is outside of [-%d, %d).", dim, ndim, ndim));
|
||||
if (dim < 0) {
|
||||
return dim + ndim;
|
||||
}
|
||||
return dim;
|
||||
}
|
||||
|
||||
// Refer to https://stackoverflow.com/a/5289170
|
||||
template <typename Range, typename Value = typename Range::value_type>
|
||||
std::string str_join(Range const& elements,
|
||||
const std::string& delimiter = ",") {
|
||||
std::ostringstream os;
|
||||
auto b = std::begin(elements), e = std::end(elements);
|
||||
|
||||
if (b != e) {
|
||||
std::copy(b, prev(e), std::ostream_iterator<Value>(os, delimiter.c_str()));
|
||||
b = prev(e);
|
||||
}
|
||||
if (b != e) {
|
||||
os << *b;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline std::string str_join(std::map<std::string, bool> const& elements,
|
||||
const std::string& delimiter = ",") {
|
||||
std::string str;
|
||||
for (const auto& item : elements) {
|
||||
str += item.first + ": " + std::to_string(item.second) + delimiter;
|
||||
}
|
||||
return str.substr(0, str.size() - 1);
|
||||
}
|
||||
|
||||
inline std::string str_join(const std::vector<std::vector<int64_t>>& elements) {
|
||||
std::stringstream ss;
|
||||
for (const auto& e : elements) {
|
||||
ss << "[" << str_join(e) << "] ";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::string str_join(const std::unordered_map<int64_t, int64_t>& map) {
|
||||
std::stringstream ss;
|
||||
for (const auto& [k, v] : map) {
|
||||
ss << "mesh dim: " << std::to_string(k)
|
||||
<< ", split factor: " << std::to_string(v);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Refer to https://stackoverflow.com/a/46931770
|
||||
inline std::vector<std::string> str_split(std::string const& input,
|
||||
const std::string& delimiter = ",") {
|
||||
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
|
||||
std::string token;
|
||||
std::vector<std::string> output;
|
||||
while ((pos_end = input.find(delimiter, pos_start)) != std::string::npos) {
|
||||
token = input.substr(pos_start, pos_end - pos_start);
|
||||
pos_start = pos_end + delim_len;
|
||||
output.push_back(token);
|
||||
}
|
||||
output.push_back(input.substr(pos_start));
|
||||
return output;
|
||||
}
|
||||
|
||||
// Refer to https://stackoverflow.com/a/29200671/2358969
|
||||
template <typename T>
|
||||
std::string to_string_with_precision(const T a_value, const int n = 2) {
|
||||
std::ostringstream out;
|
||||
out.precision(n);
|
||||
out << std::fixed << a_value;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
class SplitFactor final {
|
||||
public:
|
||||
SplitFactor() {}
|
||||
SplitFactor(std::unordered_map<int64_t, int64_t> split_factor_map)
|
||||
: split_factor_map_(split_factor_map) {}
|
||||
|
||||
void set_split_factor(int64_t mesh_dim, int64_t split_factor) {
|
||||
// default value is 1
|
||||
if (split_factor > 1) {
|
||||
split_factor_map_[mesh_dim] = split_factor;
|
||||
}
|
||||
PADDLE_ENFORCE_LE(split_factor_map_.size(),
|
||||
1,
|
||||
common::errors::InvalidArgument(
|
||||
"At now only support to rearrange at one mesh dim."));
|
||||
}
|
||||
|
||||
int64_t get_split_factor(int64_t mesh_dim) const {
|
||||
return split_factor_map_.count(mesh_dim) ? split_factor_map_.at(mesh_dim)
|
||||
: 1;
|
||||
}
|
||||
|
||||
void clear_split_factor(int64_t mesh_dim) {
|
||||
if (split_factor_map_.count(mesh_dim)) {
|
||||
split_factor_map_.erase(mesh_dim);
|
||||
}
|
||||
}
|
||||
bool operator==(const SplitFactor& other) const {
|
||||
return split_factor_map_ == other.split_factor_map_;
|
||||
}
|
||||
|
||||
bool operator!=(const SplitFactor& other) const {
|
||||
return !(this->operator==(other));
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
std::stringstream ss;
|
||||
for (const auto& [k, v] : split_factor_map_) {
|
||||
ss << "mesh dim: " << std::to_string(k)
|
||||
<< ", split factor: " << std::to_string(v);
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<int64_t, int64_t> split_factor_map_;
|
||||
};
|
||||
|
||||
} // namespace auto_parallel
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<phi::distributed::auto_parallel::SplitFactor> {
|
||||
size_t operator()(
|
||||
const phi::distributed::auto_parallel::SplitFactor& split_factor) const {
|
||||
string str = split_factor.to_string();
|
||||
return hash<string>()(str);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
Reference in New Issue
Block a user