chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
add_subdirectory(check)
|
||||
add_subdirectory(store)
|
||||
add_subdirectory(auto_parallel)
|
||||
add_subdirectory(collective)
|
||||
|
||||
set(DISTRIBUTED_COMMON_SRCS comm_context_manager.cc utils.cc)
|
||||
|
||||
if(WITH_NCCL OR WITH_RCCL)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS comm_task_manager.cc)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS nccl_comm_context.cc nccl_comm_task.cc
|
||||
nccl_tools.cc)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS nccl_config.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_GLOO)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS gloo_utils.cc gloo_comm_context.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_CUSTOM_DEVICE)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS xccl_comm_context.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_XPU_BKCL)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS bkcl_comm_context.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_FLAGCX)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS flagcx_tools.cc)
|
||||
if(NOT WITH_XPU)
|
||||
list(APPEND DISTRIBUTED_COMMON_SRCS flagcx_comm_context.cc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
collect_srcs(core_srcs SRCS ${DISTRIBUTED_COMMON_SRCS})
|
||||
@@ -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
|
||||
@@ -0,0 +1,396 @@
|
||||
// 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/bkcl_comm_context.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/distributed/check/static_check.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
BKCLCommContext::BKCLCommContext(int rank, int size, BKCLUniqueId bkcl_id)
|
||||
: CommContext(rank, size) {
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(
|
||||
bkcl_init_rank(&bkcl_comm_, rank_, size_, &bkcl_id));
|
||||
}
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
BKCLCommContext::BKCLCommContext(int rank,
|
||||
int size,
|
||||
flagcxHandlerGroup_t flagcx_handler)
|
||||
: CommContext(rank, size), flagcx_handler_(flagcx_handler) {
|
||||
phi::dynload::flagcxCommInitRank(
|
||||
&flagcx_handler_->comm, size_, flagcx_handler_->uniqueId, rank_);
|
||||
}
|
||||
#endif
|
||||
|
||||
BKCLContext_t BKCLCommContext::GetBKCLComm() { return bkcl_comm_; }
|
||||
|
||||
XPUStream BKCLCommContext::GetStream() { return dev_ctx_->stream(); }
|
||||
|
||||
phi::XPUContext* BKCLCommContext::GetDevContext() { return dev_ctx_.get(); }
|
||||
|
||||
void BKCLCommContext::SetDevContext(
|
||||
std::unique_ptr<phi::XPUContext>&& dev_ctx) {
|
||||
dev_ctx_ = std::move(dev_ctx);
|
||||
}
|
||||
|
||||
XPUEvent BKCLCommContext::GetComputeEvent() { return compute_event_.get(); }
|
||||
|
||||
void BKCLCommContext::SetComputeEvent(
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type>&& compute_event) {
|
||||
compute_event_ = std::move(compute_event);
|
||||
}
|
||||
|
||||
XPUEvent BKCLCommContext::GetCommEvent() { return comm_event_.get(); }
|
||||
|
||||
void BKCLCommContext::SetCommEvent(
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type>&& comm_event) {
|
||||
comm_event_ = std::move(comm_event);
|
||||
}
|
||||
|
||||
void BKCLCommContext::Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
XPUStream stream) {
|
||||
CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxBroadcast(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
root,
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_broadcast(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
root,
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::GatherLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxAllGather(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_all_gather(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
in_tensor.numel(),
|
||||
out_tensor->data(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::ScatterLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(phi::dynload::flagcxReduceScatter(
|
||||
in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
out_tensor->numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
BkclToFlagcxRedType(reduce_type),
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(
|
||||
bkcl_reduce_scatter(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
out_tensor->numel(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
void BKCLCommContext::Scatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::ScatterLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxScatter(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
out_tensor->numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
root,
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
}
|
||||
#endif
|
||||
|
||||
void BKCLCommContext::Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(
|
||||
in_tensor, rank_, size_, AllocationType::XPU);
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxSend(in_tensor.data(),
|
||||
count,
|
||||
ToFlagcxDataType(in_tensor.dtype()),
|
||||
peer,
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_send(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
count,
|
||||
peer,
|
||||
ToBKCLDataType(in_tensor.dtype()),
|
||||
stream));
|
||||
#endif
|
||||
VLOG(3) << "rank " << GetRank() << " send " << phi::product(in_tensor.dims())
|
||||
<< " to " << peer;
|
||||
}
|
||||
|
||||
void BKCLCommContext::Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(
|
||||
*out_tensor, rank_, size_, AllocationType::XPU);
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxRecv(out_tensor->data(),
|
||||
count,
|
||||
ToFlagcxDataType(out_tensor->dtype()),
|
||||
peer,
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_recv(bkcl_comm_,
|
||||
out_tensor->data(),
|
||||
count,
|
||||
peer,
|
||||
ToBKCLDataType(out_tensor->dtype()),
|
||||
stream));
|
||||
#endif
|
||||
VLOG(3) << "rank " << GetRank() << " recv "
|
||||
<< common::product(out_tensor->dims()) << " from " << peer;
|
||||
}
|
||||
|
||||
void BKCLCommContext::AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxAllReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
BkclToFlagcxRedType(reduce_type),
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_all_reduce(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::AllToAll(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxAlltoAll(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel() / size_,
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_all_to_all(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
in_tensor.numel() / size_,
|
||||
out_tensor->data(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::AllToAllUnequalSplit(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const DenseTensor& out_size_tensor,
|
||||
const DenseTensor& out_offset_tensor,
|
||||
const DenseTensor& in_size_tensor,
|
||||
const DenseTensor& in_offset_tensor,
|
||||
XPUStream stream) {
|
||||
auto in_size_ptr = reinterpret_cast<const size_t*>(in_size_tensor.data());
|
||||
auto in_offset_ptr = reinterpret_cast<const size_t*>(in_offset_tensor.data());
|
||||
auto out_size_ptr = reinterpret_cast<const size_t*>(out_size_tensor.data());
|
||||
auto out_offset_ptr =
|
||||
reinterpret_cast<const size_t*>(out_offset_tensor.data());
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxAlltoAllv(in_tensor.data(),
|
||||
const_cast<size_t*>(in_size_ptr),
|
||||
const_cast<size_t*>(in_offset_ptr),
|
||||
out_tensor->data(),
|
||||
const_cast<size_t*>(out_size_ptr),
|
||||
const_cast<size_t*>(out_offset_ptr),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(
|
||||
bkcl_all_to_all_v(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
in_size_ptr,
|
||||
in_offset_ptr,
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
out_tensor->data(),
|
||||
out_size_ptr,
|
||||
out_offset_ptr,
|
||||
ToBKCLDataType(out_tensor->type()),
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
int root,
|
||||
XPUStream stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ root,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::XPU);
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
BkclToFlagcxRedType(reduce_type),
|
||||
root,
|
||||
flagcx_handler_->comm,
|
||||
reinterpret_cast<flagcxStream_t>(&stream)));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_reduce(bkcl_comm_,
|
||||
in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToBKCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
root,
|
||||
stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
void BKCLCommContext::GroupStart() {
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(phi::dynload::flagcxGroupStart(flagcx_handler_->comm));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_group_start());
|
||||
#endif
|
||||
}
|
||||
void BKCLCommContext::GroupEnd() {
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
FLAGCX_CHECK(phi::dynload::flagcxGroupEnd(flagcx_handler_->comm));
|
||||
#else
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_group_end());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
flagcxRedOp_t BKCLCommContext::BkclToFlagcxRedType(BKCLOp redOp) {
|
||||
switch (redOp) {
|
||||
case BKCL_MIN:
|
||||
return flagcxMin;
|
||||
case BKCL_MAX:
|
||||
return flagcxMax;
|
||||
case BKCL_ADD:
|
||||
return flagcxSum;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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/backends/xpu/enforce_xpu.h"
|
||||
#include "paddle/phi/backends/xpu/xpu_context.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
#include "paddle/phi/backends/dynload/flagcx.h"
|
||||
#include "paddle/phi/core/distributed/flagcx_tools.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
class DenseTensor;
|
||||
namespace distributed {
|
||||
|
||||
class BKCLCommContext final : public CommContext {
|
||||
public:
|
||||
BKCLCommContext(int rank, int size, BKCLUniqueId BKCL_id);
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
BKCLCommContext(int rank, int size, flagcxHandlerGroup_t flagcx_handler);
|
||||
#endif
|
||||
~BKCLCommContext() override = default;
|
||||
|
||||
BKCLContext_t GetBKCLComm();
|
||||
|
||||
XPUStream GetStream();
|
||||
|
||||
XPUEvent GetComputeEvent();
|
||||
|
||||
void SetComputeEvent(
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type>&& compute_event);
|
||||
|
||||
XPUEvent GetCommEvent();
|
||||
|
||||
void SetCommEvent(
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type>&& comm_event);
|
||||
|
||||
phi::XPUContext* GetDevContext();
|
||||
|
||||
void SetDevContext(std::unique_ptr<phi::XPUContext>&& dev_ctx);
|
||||
|
||||
void Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
XPUStream stream);
|
||||
|
||||
void Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
XPUStream stream);
|
||||
|
||||
void Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
XPUStream stream);
|
||||
|
||||
void ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
XPUStream stream);
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
void Scatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
XPUStream stream);
|
||||
#endif
|
||||
|
||||
void AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
XPUStream stream);
|
||||
|
||||
void AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
XPUStream stream);
|
||||
|
||||
void AllToAll(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
XPUStream stream);
|
||||
|
||||
void AllToAllUnequalSplit(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const DenseTensor& out_size_tensor,
|
||||
const DenseTensor& out_offset_tensor,
|
||||
const DenseTensor& in_size_tensor,
|
||||
const DenseTensor& in_offset_tensor,
|
||||
XPUStream stream);
|
||||
|
||||
void Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
BKCLOp reduce_type,
|
||||
int root,
|
||||
XPUStream stream);
|
||||
|
||||
void GroupStart();
|
||||
|
||||
void GroupEnd();
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
flagcxRedOp_t BkclToFlagcxRedType(BKCLOp redOp);
|
||||
#endif
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(BKCLCommContext);
|
||||
|
||||
BKCLContext_t bkcl_comm_;
|
||||
|
||||
std::unique_ptr<phi::XPUContext> dev_ctx_;
|
||||
|
||||
// used for comm wait compute, compute_stream-->event-->comm_stream
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type> compute_event_;
|
||||
|
||||
// used for compute wait comm, comm_stream-->event-->compute_stream
|
||||
std::shared_ptr<std::remove_pointer<XPUEvent>::type> comm_event_;
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
|
||||
public:
|
||||
flagcxHandlerGroup_t flagcx_handler_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,11 @@
|
||||
set(CHECK_COMMON_SRCS static_check.cc)
|
||||
|
||||
if(WITH_NCCL OR WITH_RCCL)
|
||||
list(APPEND CHECK_COMMON_SRCS nccl_dynamic_check.cc)
|
||||
endif()
|
||||
|
||||
if(WITH_XPU_BKCL)
|
||||
list(APPEND CHECK_COMMON_SRCS bkcl_dynamic_check.cc)
|
||||
endif()
|
||||
|
||||
collect_srcs(core_srcs SRCS ${CHECK_COMMON_SRCS})
|
||||
@@ -0,0 +1,156 @@
|
||||
// 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/check/bkcl_dynamic_check.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/utils/string/string_helper.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
void BKCLDynamicCheck::CheckDataType(const DenseTensor& tensor, int64_t dtype) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
static_cast<int64_t>(tensor.dtype()),
|
||||
dtype,
|
||||
common::errors::InvalidArgument(
|
||||
"Tensors in communication are expected to have the same data type."));
|
||||
}
|
||||
|
||||
void BKCLDynamicCheck::CheckDataType(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
BKCLContext_t comm) {
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
int64_t dtype_host = static_cast<int64_t>(tensor.dtype());
|
||||
int64_t* dtype_device;
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(
|
||||
xpu_malloc(reinterpret_cast<void**>(&dtype_device), kSize));
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(
|
||||
dtype_device, &dtype_host, kSize, XPUMemcpyKind::XPU_HOST_TO_DEVICE));
|
||||
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_broadcast(
|
||||
comm, dtype_device, dtype_device, 1, BKCL_INT64, root_rank, 0));
|
||||
|
||||
if (root_rank == cur_rank) {
|
||||
VLOG(3) << "Dynamic check broadcast metadata, dtype: " << dtype_host;
|
||||
} else {
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(
|
||||
&dtype_host, dtype_device, kSize, XPUMemcpyKind::XPU_DEVICE_TO_HOST));
|
||||
VLOG(3) << "Dynamic check recv metadata, dtype: " << dtype_host;
|
||||
CheckDataType(tensor, dtype_host);
|
||||
}
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_free(dtype_device));
|
||||
}
|
||||
|
||||
void BKCLDynamicCheck::CheckShape(const DenseTensor& tensor, int64_t shape) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
tensor.numel(),
|
||||
shape,
|
||||
common::errors::InvalidArgument(
|
||||
"Tensors in communication are expected to have matching sizes."));
|
||||
}
|
||||
|
||||
void BKCLDynamicCheck::CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const std::vector<int64_t>& in_size_each_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
BKCLContext_t comm) {
|
||||
CheckDataType(out_tensor, /*root_rank*/ 0, cur_rank, comm);
|
||||
CheckDataType(in_tensor, /*root_rank*/ 0, cur_rank, comm);
|
||||
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
int64_t in_row_size =
|
||||
in_tensor.dims()[0] == 0 ? 0 : in_tensor.numel() / in_tensor.dims()[0];
|
||||
|
||||
for (int rank = 0; rank < world_size; ++rank) {
|
||||
int64_t in_shape_host = in_size_each_rank[rank] * in_row_size;
|
||||
int64_t* in_shape_device;
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(
|
||||
xpu_malloc(reinterpret_cast<void**>(&in_shape_device), kSize));
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(in_shape_device,
|
||||
&in_shape_host,
|
||||
kSize,
|
||||
XPUMemcpyKind::XPU_HOST_TO_DEVICE));
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_reduce(comm,
|
||||
in_shape_device,
|
||||
in_shape_device,
|
||||
1,
|
||||
BKCL_INT64,
|
||||
BKCL_ADD,
|
||||
rank,
|
||||
0));
|
||||
if (rank == cur_rank) {
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(&in_shape_host,
|
||||
in_shape_device,
|
||||
kSize,
|
||||
XPUMemcpyKind::XPU_DEVICE_TO_HOST));
|
||||
VLOG(3) << "Dynamic check recv metadata, shape: " << in_shape_host;
|
||||
CheckShape(out_tensor, in_shape_host);
|
||||
}
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_free(in_shape_device));
|
||||
}
|
||||
}
|
||||
|
||||
void BKCLDynamicCheck::CheckAlltoAllShape(
|
||||
const std::vector<DenseTensor>& out_tensor,
|
||||
const std::vector<DenseTensor>& in_tensor,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
BKCLContext_t comm) {
|
||||
int64_t first_dtype = static_cast<int64_t>(in_tensor[0].dtype());
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
CheckDataType(in_tensor[0], /*root_rank*/ 0, cur_rank, comm);
|
||||
for (int rank = 0; rank < world_size; ++rank) {
|
||||
CheckDataType(in_tensor[rank], first_dtype);
|
||||
CheckDataType(out_tensor[rank], first_dtype);
|
||||
|
||||
int64_t in_shape_host = in_tensor[rank].numel();
|
||||
int64_t* in_shape_device;
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_malloc(
|
||||
reinterpret_cast<void**>(&in_shape_device), kSize * world_size));
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(in_shape_device + cur_rank,
|
||||
&in_shape_host,
|
||||
kSize,
|
||||
XPUMemcpyKind::XPU_HOST_TO_DEVICE));
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_all_gather(
|
||||
comm, in_shape_device + cur_rank, 1, in_shape_device, BKCL_INT64, 0));
|
||||
if (rank == cur_rank) {
|
||||
std::vector<int64_t> in_shapes_recv_host(world_size);
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_wait());
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_memcpy(in_shapes_recv_host.data(),
|
||||
in_shape_device,
|
||||
kSize * world_size,
|
||||
XPUMemcpyKind::XPU_DEVICE_TO_HOST));
|
||||
VLOG(3) << "Dynamic check recv metadata, shape: "
|
||||
<< paddle::string::join_strings(in_shapes_recv_host, ',');
|
||||
for (int out_rank = 0; out_rank < world_size; ++out_rank) {
|
||||
CheckShape(out_tensor[out_rank], in_shapes_recv_host[out_rank]);
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_XPU_SUCCESS(xpu_free(in_shape_device));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2025 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 <vector>
|
||||
|
||||
#include "paddle/phi/backends/xpu/xpu_context.h"
|
||||
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
|
||||
|
||||
namespace phi {
|
||||
// forward declaration
|
||||
class DenseTensor;
|
||||
|
||||
namespace distributed {
|
||||
struct BKCLDynamicCheck {
|
||||
static void CheckDataType(const DenseTensor& tensor, int64_t dtype);
|
||||
|
||||
static void CheckDataType(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
BKCLContext_t comm);
|
||||
|
||||
static void CheckShape(const DenseTensor& tensor, int64_t shape);
|
||||
static void CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const std::vector<int64_t>& in_size_each_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
BKCLContext_t comm);
|
||||
static void CheckAlltoAllShape(const std::vector<DenseTensor>& out_tensor,
|
||||
const std::vector<DenseTensor>& in_tensor,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
BKCLContext_t comm);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,242 @@
|
||||
// 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/check/nccl_dynamic_check.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/utils/string/string_helper.h"
|
||||
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
#include <hip/hip_runtime.h>
|
||||
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
|
||||
#define gpuMalloc hipMalloc
|
||||
#define gpuMemcpy hipMemcpy
|
||||
#define gpuMemcpyDeviceToHost hipMemcpyDeviceToHost
|
||||
#define gpuMemcpyHostToDevice hipMemcpyHostToDevice
|
||||
#define gpuFree hipFree
|
||||
#else
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
|
||||
#define gpuMalloc cudaMalloc
|
||||
#define gpuMemcpy cudaMemcpy
|
||||
#define gpuMemcpyDeviceToHost cudaMemcpyDeviceToHost
|
||||
#define gpuMemcpyHostToDevice cudaMemcpyHostToDevice
|
||||
#define gpuFree cudaFree
|
||||
#endif
|
||||
|
||||
namespace phi::distributed {
|
||||
void NCCLDynamicCheck::CheckDataType(const DenseTensor& tensor, int64_t dtype) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
static_cast<int64_t>(tensor.dtype()),
|
||||
dtype,
|
||||
common::errors::InvalidArgument(
|
||||
"Tensors in communication are expected to have the same data type."));
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckDataType(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
ncclComm_t comm) {
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
int64_t dtype_host = static_cast<int64_t>(tensor.dtype());
|
||||
int64_t* dtype_device;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMalloc(&dtype_device, kSize));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuMemcpy(dtype_device, &dtype_host, kSize, gpuMemcpyHostToDevice));
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclBroadcast(dtype_device,
|
||||
dtype_device,
|
||||
1,
|
||||
ncclInt64,
|
||||
root_rank,
|
||||
comm,
|
||||
kDefaultStream));
|
||||
|
||||
if (root_rank == cur_rank) {
|
||||
VLOG(3) << "Dynamic check broadcast metadata, dtype: " << dtype_host;
|
||||
} else {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuMemcpy(&dtype_host, dtype_device, kSize, gpuMemcpyDeviceToHost));
|
||||
VLOG(3) << "Dynamic check recv metadata, dtype: " << dtype_host;
|
||||
CheckDataType(tensor, dtype_host);
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuFree(dtype_device));
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckShape(const DenseTensor& tensor, int64_t shape) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
tensor.numel(),
|
||||
shape,
|
||||
common::errors::InvalidArgument(
|
||||
"Tensors in communication are expected to have matching sizes."));
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckShape(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
ncclComm_t comm) {
|
||||
CheckDataType(tensor, root_rank, cur_rank, comm);
|
||||
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
int64_t shape_host = tensor.numel();
|
||||
int64_t* shape_device;
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMalloc(&shape_device, kSize));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuMemcpy(shape_device, &shape_host, kSize, gpuMemcpyHostToDevice));
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclBroadcast(shape_device,
|
||||
shape_device,
|
||||
1,
|
||||
ncclInt64,
|
||||
root_rank,
|
||||
comm,
|
||||
kDefaultStream));
|
||||
|
||||
if (root_rank == cur_rank) {
|
||||
VLOG(3) << "Dynamic check broadcast metadata, shape: " << shape_host;
|
||||
} else {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuMemcpy(&shape_host, shape_device, kSize, gpuMemcpyDeviceToHost));
|
||||
VLOG(3) << "Dynamic check recv metadata, shape: " << shape_host;
|
||||
CheckShape(tensor, shape_host);
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuFree(shape_device));
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const std::vector<int64_t>& in_size_each_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm) {
|
||||
CheckDataType(out_tensor, /*root_rank*/ 0, cur_rank, comm);
|
||||
CheckDataType(in_tensor, /*root_rank*/ 0, cur_rank, comm);
|
||||
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
int64_t in_row_size =
|
||||
in_tensor.dims()[0] == 0 ? 0 : in_tensor.numel() / in_tensor.dims()[0];
|
||||
|
||||
for (int rank = 0; rank < world_size; ++rank) {
|
||||
int64_t in_shape_host = in_size_each_rank[rank] * in_row_size;
|
||||
int64_t* in_shape_device;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMalloc(&in_shape_device, kSize));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(
|
||||
in_shape_device, &in_shape_host, kSize, gpuMemcpyHostToDevice));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclReduce(in_shape_device,
|
||||
in_shape_device,
|
||||
1,
|
||||
ncclInt64,
|
||||
ncclSum,
|
||||
rank,
|
||||
comm,
|
||||
kDefaultStream));
|
||||
if (rank == cur_rank) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(
|
||||
&in_shape_host, in_shape_device, kSize, gpuMemcpyDeviceToHost));
|
||||
VLOG(3) << "Dynamic check recv metadata, shape: " << in_shape_host;
|
||||
CheckShape(out_tensor, in_shape_host);
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuFree(in_shape_device));
|
||||
}
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckAlltoAllShape(
|
||||
const std::vector<DenseTensor>& out_tensor,
|
||||
const std::vector<DenseTensor>& in_tensor,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm) {
|
||||
int64_t first_dtype = static_cast<int64_t>(in_tensor[0].dtype());
|
||||
constexpr int kSize = sizeof(int64_t);
|
||||
CheckDataType(in_tensor[0], /*root_rank*/ 0, cur_rank, comm);
|
||||
for (int rank = 0; rank < world_size; ++rank) {
|
||||
CheckDataType(in_tensor[rank], first_dtype);
|
||||
CheckDataType(out_tensor[rank], first_dtype);
|
||||
|
||||
int64_t in_shape_host = in_tensor[rank].numel();
|
||||
int64_t* in_shape_device;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMalloc(&in_shape_device, kSize * world_size));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(in_shape_device + cur_rank,
|
||||
&in_shape_host,
|
||||
kSize,
|
||||
gpuMemcpyHostToDevice));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
phi::dynload::ncclAllGather(in_shape_device + cur_rank,
|
||||
in_shape_device,
|
||||
1,
|
||||
ncclInt64,
|
||||
comm,
|
||||
kDefaultStream));
|
||||
if (rank == cur_rank) {
|
||||
std::vector<int64_t> in_shapes_recv_host(world_size);
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(in_shapes_recv_host.data(),
|
||||
in_shape_device,
|
||||
kSize * world_size,
|
||||
gpuMemcpyDeviceToHost));
|
||||
VLOG(3) << "Dynamic check recv metadata, shape: "
|
||||
<< paddle::string::join_strings(in_shapes_recv_host, ',');
|
||||
for (int out_rank = 0; out_rank < world_size; ++out_rank) {
|
||||
CheckShape(out_tensor[out_rank], in_shapes_recv_host[out_rank]);
|
||||
}
|
||||
}
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuFree(in_shape_device));
|
||||
}
|
||||
}
|
||||
|
||||
void NCCLDynamicCheck::CheckGatherShape(
|
||||
const DenseTensor& in_tensor,
|
||||
const std::vector<DenseTensor>& out_tensors,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm) {
|
||||
std::vector<int64_t> shapes(world_size, 0);
|
||||
shapes[cur_rank] = in_tensor.numel();
|
||||
int64_t* in_shape_device;
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(
|
||||
gpuMalloc(&in_shape_device, world_size * sizeof(int64_t)));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(in_shape_device,
|
||||
shapes.data(),
|
||||
world_size * sizeof(int64_t),
|
||||
gpuMemcpyHostToDevice));
|
||||
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclAllReduce(in_shape_device,
|
||||
in_shape_device,
|
||||
world_size,
|
||||
ncclInt64,
|
||||
ncclSum,
|
||||
comm,
|
||||
kDefaultStream));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuMemcpy(shapes.data(),
|
||||
in_shape_device,
|
||||
world_size * sizeof(int64_t),
|
||||
gpuMemcpyDeviceToHost));
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(gpuFree(in_shape_device));
|
||||
|
||||
if (cur_rank == root_rank) {
|
||||
for (int i = 0; i < world_size; i++) {
|
||||
CheckShape(out_tensors[i], shapes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include "paddle/phi/backends/gpu/forwards.h"
|
||||
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
using gpuStream_t = hipStream_t;
|
||||
#else
|
||||
using gpuStream_t = cudaStream_t;
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
// forward declaration
|
||||
class DenseTensor;
|
||||
|
||||
namespace distributed {
|
||||
struct NCCLDynamicCheck {
|
||||
static void CheckDataType(const DenseTensor& tensor, int64_t dtype);
|
||||
|
||||
static void CheckDataType(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
ncclComm_t comm);
|
||||
|
||||
static void CheckShape(const DenseTensor& tensor, int64_t shape);
|
||||
|
||||
static void CheckShape(const DenseTensor& tensor,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
ncclComm_t comm);
|
||||
|
||||
static void CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
const std::vector<int64_t>& in_size_each_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm);
|
||||
|
||||
static void CheckAlltoAllShape(const std::vector<DenseTensor>& out_tensor,
|
||||
const std::vector<DenseTensor>& in_tensor,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm);
|
||||
|
||||
// can be used to check gather and all gather
|
||||
static void CheckGatherShape(const DenseTensor& in_tensor,
|
||||
const std::vector<DenseTensor>& out_tensors,
|
||||
int root_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
ncclComm_t comm);
|
||||
|
||||
private:
|
||||
// `0` represents default stream for both cuda & hip
|
||||
static constexpr gpuStream_t kDefaultStream = 0;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,215 @@
|
||||
// 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/check/static_check.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
void CommStaticCheck::CheckRank(int rank, int world_size) {
|
||||
PADDLE_ENFORCE_GE(rank,
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"Rank should be greater than or equal to 0."));
|
||||
PADDLE_ENFORCE_LT(
|
||||
rank,
|
||||
world_size,
|
||||
common::errors::InvalidArgument("Rank is out of the process group."));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckPlace(const DenseTensor& tensor,
|
||||
AllocationType place) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
tensor.place().GetType(),
|
||||
place,
|
||||
common::errors::InvalidArgument("Tensor should be in backend's place."));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckPlace(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
AllocationType place) {
|
||||
CheckPlace(out_tensor, place);
|
||||
CheckPlace(in_tensor, place);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
out_tensor.place(),
|
||||
in_tensor.place(),
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should be on the same place."));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckDataType(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
out_tensor.dtype(),
|
||||
in_tensor.dtype(),
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should have the same data type."));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckDataType(
|
||||
const std::vector<DenseTensor>& out_tensors,
|
||||
const std::vector<DenseTensor>& in_tensors) {
|
||||
if (in_tensors.empty() && out_tensors.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto first_dtype =
|
||||
(in_tensors.empty() ? out_tensors[0] : in_tensors[0]).dtype();
|
||||
|
||||
for (const auto& tensor : in_tensors) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
tensor.dtype(),
|
||||
first_dtype,
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should have the same data type."));
|
||||
}
|
||||
for (const auto& tensor : out_tensors) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
tensor.dtype(),
|
||||
first_dtype,
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should have the same data type."));
|
||||
}
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckShape(const DenseTensor& tensor) {
|
||||
PADDLE_ENFORCE_GT(tensor.numel(),
|
||||
0,
|
||||
common::errors::InvalidArgument(
|
||||
"Size of tensor should be greater than 0."));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int out_size_factor,
|
||||
int in_size_factor) {
|
||||
CheckShape(out_tensor);
|
||||
CheckShape(in_tensor);
|
||||
int64_t out_size = out_tensor.numel(), in_size = in_tensor.numel();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
out_size * out_size_factor,
|
||||
in_size * in_size_factor,
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should have matching sizes. "
|
||||
"out_size=%ld, out_size_factor=%d, in_size=%ld, in_size_factor=%d",
|
||||
out_size,
|
||||
out_size_factor,
|
||||
in_size,
|
||||
in_size_factor));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
int out_size_factor,
|
||||
int in_size_factor,
|
||||
AllocationType place) {
|
||||
CheckRank(dst_rank, world_size);
|
||||
CheckRank(cur_rank, world_size);
|
||||
|
||||
CheckPlace(out_tensor, in_tensor, place);
|
||||
CheckDataType(out_tensor, in_tensor);
|
||||
|
||||
if (dst_rank == cur_rank) {
|
||||
CheckShape(out_tensor, in_tensor, out_size_factor, in_size_factor);
|
||||
} else {
|
||||
CheckShape(out_tensor);
|
||||
CheckShape(in_tensor);
|
||||
}
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckShape(const DenseTensor& tensor,
|
||||
int rank,
|
||||
int world_size,
|
||||
AllocationType place) {
|
||||
CheckPlace(tensor, place);
|
||||
CheckRank(rank, world_size);
|
||||
}
|
||||
|
||||
void CommStaticCheck::SameShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place) {
|
||||
CheckShape(out_tensor,
|
||||
in_tensor,
|
||||
dst_rank,
|
||||
cur_rank,
|
||||
world_size,
|
||||
/*out_size_factor*/ 1,
|
||||
/*in_size_factor*/ 1,
|
||||
place);
|
||||
}
|
||||
|
||||
void CommStaticCheck::ScatterLikeShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place) {
|
||||
CheckShape(out_tensor,
|
||||
in_tensor,
|
||||
dst_rank,
|
||||
cur_rank,
|
||||
world_size,
|
||||
/*out_size_factor*/ world_size,
|
||||
/*in_size_factor*/ 1,
|
||||
place);
|
||||
}
|
||||
|
||||
void CommStaticCheck::GatherLikeShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place) {
|
||||
CheckRank(dst_rank, world_size);
|
||||
CheckRank(cur_rank, world_size);
|
||||
|
||||
CheckPlace(out_tensor, in_tensor, place);
|
||||
CheckDataType(out_tensor, in_tensor);
|
||||
|
||||
CheckGatherShape(out_tensor);
|
||||
CheckGatherShape(in_tensor);
|
||||
int64_t out_size = out_tensor.numel(), in_size = in_tensor.numel();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
out_size,
|
||||
in_size * world_size,
|
||||
common::errors::InvalidArgument(
|
||||
"Input and output tensors should have matching sizes. "
|
||||
"out_size=%ld, out_size_factor=%d, in_size=%ld, in_size_factor=%d",
|
||||
out_size,
|
||||
1,
|
||||
in_size,
|
||||
world_size));
|
||||
}
|
||||
|
||||
void CommStaticCheck::CheckGatherShape(const phi::DenseTensor& tensor) {
|
||||
PADDLE_ENFORCE_GE(
|
||||
tensor.numel(),
|
||||
0,
|
||||
common::errors::InvalidArgument("Size of tensor should be greater equal "
|
||||
"than 0 in gather-liked communication."));
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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/common/place.h"
|
||||
|
||||
namespace phi {
|
||||
// forward declaration
|
||||
class DenseTensor;
|
||||
|
||||
namespace distributed {
|
||||
struct CommStaticCheck {
|
||||
static void CheckRank(int rank, int world_size);
|
||||
|
||||
static void CheckPlace(const DenseTensor& tensor, AllocationType place);
|
||||
|
||||
static void CheckPlace(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
AllocationType place);
|
||||
|
||||
static void CheckDataType(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor);
|
||||
|
||||
static void CheckDataType(const std::vector<DenseTensor>& out_tensors,
|
||||
const std::vector<DenseTensor>& in_tensors);
|
||||
|
||||
static void CheckShape(const DenseTensor& tensor);
|
||||
|
||||
static void CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int out_size_factor,
|
||||
int in_size_factor);
|
||||
|
||||
static void CheckShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
int out_size_factor,
|
||||
int in_size_factor,
|
||||
AllocationType place = AllocationType::GPU);
|
||||
|
||||
// for p2p
|
||||
static void CheckShape(const DenseTensor& tensor,
|
||||
int rank,
|
||||
int world_size,
|
||||
AllocationType place = AllocationType::GPU);
|
||||
|
||||
// for collective
|
||||
static void SameShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place = AllocationType::GPU);
|
||||
|
||||
static void ScatterLikeShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place = AllocationType::GPU);
|
||||
|
||||
static void GatherLikeShape(const DenseTensor& out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int dst_rank,
|
||||
int cur_rank,
|
||||
int world_size,
|
||||
AllocationType place = AllocationType::GPU);
|
||||
|
||||
static void CheckGatherShape(const phi::DenseTensor& tensor);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,3 @@
|
||||
set(COLLECTIVE_SRCS process_group.cc)
|
||||
|
||||
collect_srcs(core_srcs SRCS ${COLLECTIVE_SRCS})
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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/collective/process_group.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
bool ProcessGroup::Task::IsCompleted() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return is_completed_;
|
||||
}
|
||||
|
||||
ProcessGroup::ProcessGroup(int rank, int size, int gid)
|
||||
: rank_(rank), size_(size), gid_(gid) {
|
||||
if (gid != kIgnoreId) {
|
||||
auto map = ProcessGroupMapFromGid::getInstance();
|
||||
map->insert(gid_, this);
|
||||
}
|
||||
const char* global_rank = std::getenv("PADDLE_TRAINER_ID");
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
global_rank,
|
||||
common::errors::NotFound(
|
||||
"The environment variable 'PADDLE_TRAINER_ID' cannot be found."));
|
||||
global_rank_ = std::atoi(global_rank);
|
||||
}
|
||||
|
||||
// TODO(sunyilun): methods below will be removed later
|
||||
ProcessGroupIdMap& ProcessGroupIdMap::GetInstance() {
|
||||
static ProcessGroupIdMap instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ProcessGroupIdMap::DestroyProcessGroup() {
|
||||
auto& id_map = ProcessGroupIdMap::GetInstance();
|
||||
for (auto& item : id_map) {
|
||||
auto use_count = item.second.use_count();
|
||||
for (int i = 0; i < use_count; ++i) {
|
||||
item.second.reset();
|
||||
}
|
||||
}
|
||||
id_map.clear();
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,598 @@
|
||||
// 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 <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/device_context.h"
|
||||
#include "paddle/phi/core/distributed/types.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
constexpr auto kWaitTimeout = std::chrono::milliseconds(0);
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
using phi::distributed::AllreduceOptions;
|
||||
using phi::distributed::BarrierOptions;
|
||||
using phi::distributed::BroadcastOptions;
|
||||
using phi::distributed::CommType;
|
||||
using phi::distributed::GatherOptions;
|
||||
using phi::distributed::GetPartialTensor;
|
||||
using phi::distributed::ReduceOp;
|
||||
using phi::distributed::ReduceOptions;
|
||||
using phi::distributed::ReduceScatterOptions;
|
||||
using phi::distributed::ScatterOptions;
|
||||
constexpr int kIgnoreId = -1;
|
||||
|
||||
class ProcessGroup {
|
||||
public:
|
||||
class Task {
|
||||
public:
|
||||
Task(int rank, CommType comm_type, bool sync_op)
|
||||
: rank_(rank), comm_type_(comm_type), sync_op_(sync_op) {}
|
||||
virtual ~Task() = default;
|
||||
|
||||
virtual bool IsCompleted();
|
||||
virtual bool Wait(std::chrono::milliseconds timeout UNUSED = kWaitTimeout) {
|
||||
return false;
|
||||
}
|
||||
virtual void Synchronize() {}
|
||||
virtual void UpdateWaitChain(const phi::DeviceContext& ctx UNUSED) {}
|
||||
|
||||
bool IsSync() const { return sync_op_; }
|
||||
|
||||
// TODO(sunyilun): methods below will be removed later
|
||||
Task(int rank,
|
||||
const std::vector<DenseTensor>& inputs UNUSED,
|
||||
CommType comm_type)
|
||||
: rank_(rank), comm_type_(comm_type) {}
|
||||
Task(int rank,
|
||||
const std::vector<DenseTensor>& inputs UNUSED,
|
||||
CommType comm_type,
|
||||
bool sync_op)
|
||||
: rank_(rank), comm_type_(comm_type), sync_op_(sync_op) {}
|
||||
|
||||
protected:
|
||||
const int rank_;
|
||||
CommType comm_type_{CommType::UNKNOWN};
|
||||
std::mutex mutex_;
|
||||
bool is_completed_{false};
|
||||
|
||||
private:
|
||||
bool sync_op_{true};
|
||||
};
|
||||
|
||||
public:
|
||||
ProcessGroup(int rank, int size, int gid);
|
||||
virtual ~ProcessGroup() = default;
|
||||
|
||||
int GetRank() const { return rank_; }
|
||||
|
||||
int GetSize() const { return size_; }
|
||||
|
||||
int GetGid() const { return gid_; }
|
||||
|
||||
std::string GetGroupMessage() const {
|
||||
return std::string("rank_in_group: ") + std::to_string(rank_) +
|
||||
std::string(", nranks: ") + std::to_string(size_) +
|
||||
std::string(", gid: ") + std::to_string(gid_) +
|
||||
std::string(", backend: ") + GetBackendName();
|
||||
}
|
||||
|
||||
virtual std::string GetBackendName() const = 0;
|
||||
|
||||
virtual DeviceContext* GetDeviceContext(const Place& place UNUSED) const {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support get device_context.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual DeviceContext* GetDeviceContext(const Place& place UNUSED,
|
||||
bool use_calc_stream UNUSED) const {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support get device_context.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual void StartCoalescing() {}
|
||||
|
||||
virtual void EndCoalescing(
|
||||
std::optional<std::vector<std::shared_ptr<ProcessGroup::Task>>>
|
||||
tasks_opt = std::nullopt) {}
|
||||
|
||||
virtual void EagerConnect() {}
|
||||
|
||||
virtual void EagerConnectRingExchange() {}
|
||||
|
||||
// without stream APIs
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_gather with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_gather with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllReduce(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const AllreduceOptions& opts UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_reduce with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllToAll(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const std::vector<int64_t>& out_size_each_rank UNUSED,
|
||||
const std::vector<int64_t>& in_size_each_rank UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_to_all with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllToAll(
|
||||
std::vector<DenseTensor>* out_tensors UNUSED,
|
||||
const std::vector<DenseTensor>& in_tensors UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_to_all with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Barrier(
|
||||
const BarrierOptions& UNUSED = BarrierOptions()) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support barrier.", GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Broadcast(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const BroadcastOptions& opts UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support broadcast with sync_op flag",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Reduce(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ReduceOptions& opts UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support reduce with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> ReduceScatter(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ReduceScatterOptions& opts UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support reduce_scatter with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Scatter(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ScatterOptions& opts UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support scatter with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Recv(DenseTensor* tensor UNUSED,
|
||||
int src_rank UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support recv with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Recv(DenseTensor* tensor UNUSED,
|
||||
int src_rank UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support recv with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Send(const DenseTensor& tensor
|
||||
UNUSED,
|
||||
int dst_rank UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support send with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Send(const DenseTensor& tensor
|
||||
UNUSED,
|
||||
int dst_rank UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support send with sync_op flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
// stream APIs
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_gather "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_gather "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllReduce(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const AllreduceOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_reduce "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllToAll(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const std::vector<int64_t>& out_size_each_rank UNUSED,
|
||||
const std::vector<int64_t>& in_size_each_rank UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_to_all "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllToAll(
|
||||
std::vector<DenseTensor>* out_tensors UNUSED,
|
||||
const std::vector<DenseTensor>& in_tensors UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support all_to_all "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Broadcast(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const BroadcastOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support broadcast "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Reduce(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ReduceOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support reduce "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> ReduceScatter(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ReduceScatterOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support reduce_scatter "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Scatter(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const ScatterOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support scatter "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Gather(
|
||||
DenseTensor* out_tensor UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const GatherOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support gather "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Gather(
|
||||
std::vector<DenseTensor>* gather_tensors_ptr UNUSED,
|
||||
const DenseTensor& in_tensor UNUSED,
|
||||
const GatherOptions& opts UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support gather "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Recv(DenseTensor* tensor UNUSED,
|
||||
int src_rank UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream
|
||||
UNUSED) {
|
||||
PADDLE_THROW(common::errors::Unimplemented(
|
||||
"ProcessGroup%s does not support recv with "
|
||||
"sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Recv(DenseTensor* tensor UNUSED,
|
||||
int src_rank UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream
|
||||
UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support recv "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Send(
|
||||
const DenseTensor& tensor UNUSED,
|
||||
int dst_rank UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support send "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Send(
|
||||
const DenseTensor& tensor UNUSED,
|
||||
int dst_rank UNUSED,
|
||||
int64_t offset UNUSED,
|
||||
int64_t numel UNUSED,
|
||||
bool sync_op UNUSED,
|
||||
bool use_calc_stream UNUSED) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("ProcessGroup%s does not support send "
|
||||
"with sync_op and use_calc_stream flag.",
|
||||
GetBackendName()));
|
||||
}
|
||||
|
||||
// legacy APIs
|
||||
// TODO(liyurui): This API will be moved later
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllReduce(
|
||||
std::vector<DenseTensor>& inputs, // NOLINT
|
||||
std::vector<DenseTensor>& outputs, // NOLINT
|
||||
const AllreduceOptions& options = AllreduceOptions()) {
|
||||
return AllReduce(outputs.data(), inputs.front(), options, false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllReduce(
|
||||
std::vector<DenseTensor>& inputs, // NOLINT
|
||||
std::vector<DenseTensor>& outputs, // NOLINT
|
||||
const AllreduceOptions& options,
|
||||
bool sync_op) {
|
||||
return AllReduce(outputs.data(), inputs.front(), options, sync_op);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllReduce(
|
||||
std::vector<DenseTensor>& inputs, // NOLINT
|
||||
std::vector<DenseTensor>& outputs, // NOLINT
|
||||
const AllreduceOptions& options,
|
||||
bool use_calc_stream,
|
||||
bool sync_op) {
|
||||
return AllReduce(
|
||||
outputs.data(), inputs.front(), options, use_calc_stream, sync_op);
|
||||
}
|
||||
|
||||
// TODO(sunyilun): methods below will be removed later
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Broadcast(
|
||||
std::vector<DenseTensor>& inputs, // NOLINT
|
||||
std::vector<DenseTensor>& outputs, // NOLINT
|
||||
const BroadcastOptions& options = BroadcastOptions()) {
|
||||
return Broadcast(outputs.data(), inputs.front(), options, false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Broadcast(
|
||||
std::vector<DenseTensor>& inputs, // NOLINT
|
||||
std::vector<DenseTensor>& outputs, // NOLINT
|
||||
const BroadcastOptions& options,
|
||||
bool sync_op) {
|
||||
return Broadcast(outputs.data(), inputs.front(), options, sync_op);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Send(
|
||||
std::vector<DenseTensor>& tensors, int dst_rank) { // NOLINT
|
||||
return Send(tensors.front(), dst_rank, false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Recv(
|
||||
std::vector<DenseTensor>& tensors, int src_rank) { // NOLINT
|
||||
return Recv(&tensors.front(), src_rank, false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
std::vector<DenseTensor>& in_tensors, // NOLINT
|
||||
std::vector<DenseTensor>& out_tensors) { // NOLINT
|
||||
return AllGather(out_tensors.data(), in_tensors.front(), false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
std::vector<DenseTensor>& in_tensors, // NOLINT
|
||||
std::vector<DenseTensor>& out_tensors, // NOLINT
|
||||
bool sync_op) {
|
||||
return AllGather(out_tensors.data(), in_tensors.front(), sync_op);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllGather(
|
||||
std::vector<DenseTensor>& in_tensors, // NOLINT
|
||||
std::vector<DenseTensor>& out_tensors, // NOLINT
|
||||
bool use_calc_stream,
|
||||
bool sync_op) {
|
||||
return AllGather(
|
||||
out_tensors.data(), in_tensors.front(), use_calc_stream, sync_op);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> AllToAll(
|
||||
std::vector<DenseTensor>&, // NOLINT
|
||||
std::vector<DenseTensor>&) { // NOLINT
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"ProcessGroup%s does not support AllToAll", GetBackendName()));
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Reduce(
|
||||
std::vector<DenseTensor>& ins, // NOLINT
|
||||
std::vector<DenseTensor>& outs, // NOLINT
|
||||
const ReduceOptions& opts) {
|
||||
return Reduce(outs.data(), ins.front(), opts, false);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<ProcessGroup::Task> Scatter(
|
||||
std::vector<DenseTensor>& ins, // NOLINT
|
||||
std::vector<DenseTensor>& outs, // NOLINT
|
||||
const ScatterOptions& opts) {
|
||||
return Scatter(outs.data(), ins.front(), opts, false);
|
||||
}
|
||||
|
||||
protected:
|
||||
int global_rank_{-1};
|
||||
int rank_;
|
||||
int size_;
|
||||
int gid_;
|
||||
};
|
||||
|
||||
class PADDLE_API ProcessGroupIdMap
|
||||
: public std::unordered_map<int, std::shared_ptr<ProcessGroup>> {
|
||||
public:
|
||||
static ProcessGroupIdMap& GetInstance();
|
||||
static void DestroyProcessGroup();
|
||||
};
|
||||
|
||||
// TODO(dev): The following method will be removed soon.
|
||||
class ProcessGroupMapFromGid {
|
||||
public:
|
||||
bool has(int gid) { return map_.find(gid) != map_.end(); }
|
||||
|
||||
void insert(int gid, ProcessGroup* pg) { map_[gid] = pg; }
|
||||
|
||||
ProcessGroup* get(int gid) {
|
||||
auto it = map_.find(gid);
|
||||
if (it == map_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static std::shared_ptr<ProcessGroupMapFromGid> getInstance() {
|
||||
static auto s_instance = std::make_shared<ProcessGroupMapFromGid>();
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
ProcessGroupMapFromGid() = default;
|
||||
~ProcessGroupMapFromGid() = default;
|
||||
|
||||
private:
|
||||
std::unordered_map<int, ProcessGroup*> map_;
|
||||
};
|
||||
|
||||
static void CheckTensorContiguous(const DenseTensor& tensor) {
|
||||
if (!tensor.meta().is_contiguous()) {
|
||||
PADDLE_THROW(
|
||||
common::errors::InvalidArgument("The tensor must be contiguous"));
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckTensorContiguous(const std::vector<DenseTensor>& inputs) {
|
||||
for (const auto& tensor : inputs) {
|
||||
if (!tensor.meta().is_contiguous()) {
|
||||
PADDLE_THROW(
|
||||
common::errors::InvalidArgument("The tensor must be contiguous"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 "paddle/common/macros.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class CommContext {
|
||||
public:
|
||||
CommContext(int rank, int size) : rank_(rank), size_(size) {}
|
||||
virtual ~CommContext() = default;
|
||||
|
||||
int GetRank() const { return rank_; }
|
||||
int GetSize() const { return size_; }
|
||||
|
||||
protected:
|
||||
int rank_;
|
||||
int size_;
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(CommContext);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,437 @@
|
||||
// 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/comm_context_manager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/core/distributed/store/store.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
#if defined(PADDLE_WITH_GLOO)
|
||||
#include <gloo/rendezvous/prefix_store.h>
|
||||
#include "paddle/phi/core/distributed/gloo_comm_context.h"
|
||||
#include "paddle/phi/core/distributed/gloo_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/gloo_store.h"
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/context_pool.h"
|
||||
#include "paddle/phi/common/memory_utils.h"
|
||||
#include "paddle/phi/core/distributed/nccl_comm_context.h"
|
||||
#include "paddle/phi/core/distributed/nccl_tools.h"
|
||||
#elif defined(PADDLE_WITH_XPU_BKCL)
|
||||
#include "paddle/phi/backends/xpu/xpu_info.h"
|
||||
#include "paddle/phi/common/memory_utils.h"
|
||||
#include "paddle/phi/core/distributed/bkcl_comm_context.h"
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_CUSTOM_DEVICE
|
||||
#include "paddle/phi/core/distributed/xccl_comm_context.h"
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
#if !defined(PADDLE_WITH_XPU)
|
||||
#include "paddle/phi/core/distributed/flagcx_comm_context.h"
|
||||
#endif
|
||||
#include "paddle/phi/backends/dynload/flagcx.h"
|
||||
#include "paddle/phi/core/distributed/flagcx_tools.h"
|
||||
#endif
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
int CommContextManager::device_id = -1;
|
||||
|
||||
void CommContextManager::SetDeviceId(int dev_id) {
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
phi::backends::gpu::SetDeviceId(dev_id);
|
||||
CommContextManager::device_id = dev_id;
|
||||
#elif defined(PADDLE_WITH_XPU_BKCL)
|
||||
phi::backends::xpu::SetXPUDeviceId(dev_id);
|
||||
CommContextManager::device_id = dev_id;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
void CommContextManager::CreateNCCLCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key,
|
||||
const P2POption* p2p_opt,
|
||||
int nccl_comm_init_option,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr) {
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
if (comm_context_manager.Has(unique_comm_key)) {
|
||||
return;
|
||||
}
|
||||
ncclUniqueId nccl_id;
|
||||
if (rank == 0 || (p2p_opt && p2p_opt->is_p2p_op && p2p_opt->p2p_rank == 0)) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclGetUniqueId(&nccl_id));
|
||||
}
|
||||
|
||||
std::string unique_key = "NCCLCommContext/" + unique_comm_key + hash_key;
|
||||
if (rank == 0 || (p2p_opt && p2p_opt->is_p2p_op && p2p_opt->p2p_rank == 0)) {
|
||||
std::vector<uint8_t> nccl_id_wrapper(
|
||||
reinterpret_cast<uint8_t*>(&nccl_id),
|
||||
reinterpret_cast<uint8_t*>(&nccl_id) + NCCL_UNIQUE_ID_BYTES);
|
||||
store->set(unique_key, nccl_id_wrapper);
|
||||
} else {
|
||||
const auto& nccl_id_wrapper = store->get(unique_key);
|
||||
std::memcpy(&nccl_id, nccl_id_wrapper.data(), nccl_id_wrapper.size());
|
||||
}
|
||||
|
||||
if (p2p_opt) {
|
||||
rank = p2p_opt->rank;
|
||||
size = p2p_opt->num_ranks;
|
||||
}
|
||||
VLOG(3) << "init NCCLCommContext rank: " << rank << ", size: " << size
|
||||
<< ", unique_comm_key: " << unique_comm_key
|
||||
<< ", unique_key: " << unique_key
|
||||
<< ", nccl_id: " << SerializeNCCLUniqueId(nccl_id);
|
||||
auto nccl_comm_context = std::make_unique<NCCLCommContext>(
|
||||
rank, size, nccl_id, nccl_comm_init_option, nccl_config_ptr);
|
||||
if (CommContextManager::device_id != -1) {
|
||||
std::unique_ptr<phi::GPUContext> dev_ctx(
|
||||
new phi::GPUContext(phi::GPUPlace(CommContextManager::device_id)));
|
||||
dev_ctx->SetAllocator(phi::memory_utils::GetAllocator(
|
||||
CommContextManager::device_id, dev_ctx->stream()));
|
||||
dev_ctx->SetHostAllocator(phi::memory_utils::GetHostAllocator());
|
||||
dev_ctx->SetZeroAllocator(
|
||||
phi::memory_utils::GetZeroAllocator(CommContextManager::device_id));
|
||||
dev_ctx->SetHostZeroAllocator(phi::memory_utils::GetHostZeroAllocator());
|
||||
dev_ctx->SetPinnedAllocator(phi::memory_utils::GetPinnedAllocator());
|
||||
dev_ctx->PartialInitWithAllocator();
|
||||
auto compute_event =
|
||||
phi::memory_utils::GetCudaEvent(CommContextManager::device_id);
|
||||
auto comm_event =
|
||||
phi::memory_utils::GetCudaEvent(CommContextManager::device_id);
|
||||
|
||||
nccl_comm_context->SetDevContext(std::move(dev_ctx));
|
||||
nccl_comm_context->SetComputeEvent(std::move(compute_event));
|
||||
nccl_comm_context->SetCommEvent(std::move(comm_event));
|
||||
}
|
||||
|
||||
comm_context_manager.SetStore(store);
|
||||
comm_context_manager.Emplace(unique_comm_key, std::move(nccl_comm_context));
|
||||
}
|
||||
|
||||
void CommContextManager::RecreateNCCLComm(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
const std::string& recreate_key,
|
||||
const P2POption* p2p_opt) {
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
|
||||
ncclUniqueId nccl_id;
|
||||
if (rank == 0 || (p2p_opt && p2p_opt->is_p2p_op && p2p_opt->p2p_rank == 0)) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclGetUniqueId(&nccl_id));
|
||||
}
|
||||
|
||||
std::string unique_key =
|
||||
"NCCLCommContext/" + unique_comm_key + "/" + recreate_key;
|
||||
if (rank == 0 || (p2p_opt && p2p_opt->is_p2p_op && p2p_opt->p2p_rank == 0)) {
|
||||
std::vector<uint8_t> nccl_id_wrapper(
|
||||
reinterpret_cast<uint8_t*>(&nccl_id),
|
||||
reinterpret_cast<uint8_t*>(&nccl_id) + NCCL_UNIQUE_ID_BYTES);
|
||||
store->set(unique_key, nccl_id_wrapper);
|
||||
} else {
|
||||
const auto& nccl_id_wrapper = store->get(unique_key);
|
||||
std::memcpy(&nccl_id, nccl_id_wrapper.data(), nccl_id_wrapper.size());
|
||||
}
|
||||
|
||||
VLOG(3) << "RecreateNCCLComm nccl_id: " << SerializeNCCLUniqueId(nccl_id);
|
||||
|
||||
auto comm_context = static_cast<phi::distributed::NCCLCommContext*>(
|
||||
comm_context_manager.Get(unique_comm_key));
|
||||
comm_context->CreateNCCLComm(nccl_id);
|
||||
|
||||
comm_context_manager.SetStore(store);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_GLOO)
|
||||
void CommContextManager::CreateGlooCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size) {
|
||||
GlooStore store_wrapper(store);
|
||||
auto gloo_store = std::make_shared<gloo::rendezvous::PrefixStore>(
|
||||
unique_comm_key, store_wrapper);
|
||||
|
||||
auto gloo_device = CreateGlooDevice();
|
||||
|
||||
auto gloo_comm_context =
|
||||
std::make_unique<GlooCommContext>(rank, size, gloo_store, gloo_device);
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
// set actual store to manager
|
||||
comm_context_manager.SetStore(store);
|
||||
comm_context_manager.Emplace(unique_comm_key, std::move(gloo_comm_context));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_CUSTOM_DEVICE
|
||||
void CommContextManager::CreateXCCLCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
const Place& place,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key) {
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
if (comm_context_manager.Has(unique_comm_key)) {
|
||||
return;
|
||||
}
|
||||
phi::ccl::CCLRootId xccl_root_id;
|
||||
if (rank == 0) {
|
||||
phi::DeviceManager::CCLGetUniqueId(place.GetDeviceType(), &xccl_root_id);
|
||||
}
|
||||
|
||||
std::string unique_key = "XCCLCommContext/" + unique_comm_key;
|
||||
if (!hash_key.empty()) {
|
||||
unique_key += "/" + hash_key;
|
||||
}
|
||||
if (rank == 0) {
|
||||
store->set(unique_key, xccl_root_id);
|
||||
} else {
|
||||
xccl_root_id = store->get(unique_key);
|
||||
}
|
||||
VLOG(3) << "init xccl rank: " << rank << ", nranks: " << size
|
||||
<< ", unique_comm_key: " << unique_comm_key << ", xccl uniqueid: "
|
||||
<< phi::ccl::SerializeXCCLUniqueId(xccl_root_id);
|
||||
auto xccl_comm_context =
|
||||
std::make_unique<XCCLCommContext>(place, rank, size, xccl_root_id);
|
||||
if (CommContextManager::device_id != -1) {
|
||||
std::unique_ptr<phi::CustomContext> dev_ctx(
|
||||
|
||||
new phi::CustomContext(CustomPlace(place)));
|
||||
// dev_ctx->SetAllocator(phi::memory_utils::GetAllocator(
|
||||
// CommContextManager::device_id, dev_ctx->GetStream()));
|
||||
// dev_ctx->SetHostAllocator(phi::memory_utils::GetHostAllocator());
|
||||
// dev_ctx->SetZeroAllocator(
|
||||
// phi::memory_utils::GetZeroAllocator(CommContextManager::device_id));
|
||||
// dev_ctx->SetHostZeroAllocator(phi::memory_utils::GetHostZeroAllocator());
|
||||
// dev_ctx->SetPinnedAllocator(phi::memory_utils::GetPinnedAllocator());
|
||||
// dev_ctx->PartialInitWithAllocator();
|
||||
// auto compute_event =
|
||||
// phi::memory_utils::GetCudaEvent(CommContextManager::device_id);
|
||||
// auto comm_event =
|
||||
// phi::memory_utils::GetCudaEvent(CommContextManager::device_id);
|
||||
|
||||
xccl_comm_context->SetDevContext(std::move(dev_ctx));
|
||||
// xccl_comm_context->SetComputeEvent(std::move(compute_event));
|
||||
// xccl_comm_context->SetCommEvent(std::move(comm_event));
|
||||
}
|
||||
comm_context_manager.SetStore(store);
|
||||
comm_context_manager.Emplace(unique_comm_key, std::move(xccl_comm_context));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_XPU_BKCL)
|
||||
void CommContextManager::CreateBKCLCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key) {
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
if (comm_context_manager.Has(unique_comm_key)) {
|
||||
return;
|
||||
}
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
flagcxHandlerGroup_t flagcx_handler;
|
||||
phi::dynload::flagcxHandleInit(&flagcx_handler);
|
||||
if (rank == 0) {
|
||||
phi::dynload::flagcxGetUniqueId(&flagcx_handler->uniqueId);
|
||||
}
|
||||
#else
|
||||
BKCLUniqueId bkcl_id;
|
||||
if (rank == 0) {
|
||||
PADDLE_ENFORCE_BKCL_SUCCESS(bkcl_get_unique_id(&bkcl_id));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string unique_key = "BKCLCommContext/" + unique_comm_key + hash_key;
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
if (rank == 0) {
|
||||
std::vector<uint8_t> bkcl_id_wrapper(
|
||||
reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId),
|
||||
reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId) +
|
||||
sizeof(flagcxUniqueId));
|
||||
store->set(unique_key, bkcl_id_wrapper);
|
||||
} else {
|
||||
const auto& bkcl_id_wrapper = store->get(unique_key);
|
||||
std::memcpy(reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId),
|
||||
bkcl_id_wrapper.data(),
|
||||
bkcl_id_wrapper.size());
|
||||
}
|
||||
#else
|
||||
if (rank == 0) {
|
||||
std::vector<uint8_t> bkcl_id_wrapper(
|
||||
reinterpret_cast<uint8_t*>(&bkcl_id),
|
||||
reinterpret_cast<uint8_t*>(&bkcl_id) + BKCL_UNIQUE_ID_BYTES);
|
||||
store->set(unique_key, bkcl_id_wrapper);
|
||||
} else {
|
||||
const auto& bkcl_id_wrapper = store->get(unique_key);
|
||||
std::memcpy(&bkcl_id, bkcl_id_wrapper.data(), bkcl_id_wrapper.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
VLOG(3) << "init BKCLCommContext rank: " << rank << ", size: " << size
|
||||
<< ", unique_comm_key: " << unique_comm_key
|
||||
<< ", unique_key: " << unique_key;
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
auto bkcl_comm_context =
|
||||
std::make_unique<BKCLCommContext>(rank, size, flagcx_handler);
|
||||
#else
|
||||
auto bkcl_comm_context =
|
||||
std::make_unique<BKCLCommContext>(rank, size, bkcl_id);
|
||||
#endif
|
||||
|
||||
if (CommContextManager::device_id != -1) {
|
||||
std::unique_ptr<phi::XPUContext> dev_ctx(new phi::XPUContext(
|
||||
phi::XPUPlace(CommContextManager::device_id), true));
|
||||
dev_ctx->CreateStream();
|
||||
dev_ctx->SetAllocator(phi::memory_utils::GetAllocator(
|
||||
CommContextManager::device_id, dev_ctx->stream()));
|
||||
dev_ctx->SetHostAllocator(phi::memory_utils::GetHostAllocator());
|
||||
dev_ctx->SetZeroAllocator(
|
||||
phi::memory_utils::GetZeroAllocator(CommContextManager::device_id));
|
||||
dev_ctx->SetHostZeroAllocator(phi::memory_utils::GetHostZeroAllocator());
|
||||
// XPUs do not have the concept of pinned memory,
|
||||
// so the get_pinned_allocator function is not set.
|
||||
|
||||
// It currently does not support dev_ctx->PartialInitWithAllocator().
|
||||
auto compute_event =
|
||||
phi::memory_utils::GetXpuEvent(CommContextManager::device_id);
|
||||
auto comm_event =
|
||||
phi::memory_utils::GetXpuEvent(CommContextManager::device_id);
|
||||
|
||||
bkcl_comm_context->SetDevContext(std::move(dev_ctx));
|
||||
bkcl_comm_context->SetComputeEvent(std::move(compute_event));
|
||||
bkcl_comm_context->SetCommEvent(std::move(comm_event));
|
||||
}
|
||||
|
||||
comm_context_manager.SetStore(store);
|
||||
comm_context_manager.Emplace(unique_comm_key, std::move(bkcl_comm_context));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX) && !defined(PADDLE_WITH_XPU)
|
||||
void CommContextManager::CreateFlagcxCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key) {
|
||||
auto& comm_context_manager = CommContextManager::GetInstance();
|
||||
if (comm_context_manager.Has(unique_comm_key)) {
|
||||
return;
|
||||
}
|
||||
flagcxHandlerGroup_t flagcx_handler;
|
||||
phi::dynload::flagcxHandleInit(&flagcx_handler);
|
||||
if (rank == 0) {
|
||||
phi::dynload::flagcxGetUniqueId(&flagcx_handler->uniqueId);
|
||||
}
|
||||
|
||||
std::string unique_key = "XCCLCommContext/" + unique_comm_key + hash_key;
|
||||
if (rank == 0) {
|
||||
std::vector<uint8_t> flagcx_id_wrapper(
|
||||
reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId),
|
||||
reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId) +
|
||||
sizeof(flagcxUniqueId));
|
||||
store->set(unique_key, flagcx_id_wrapper);
|
||||
} else {
|
||||
const auto& flagcx_id_wrapper = store->get(unique_key);
|
||||
std::memcpy(reinterpret_cast<uint8_t*>(flagcx_handler->uniqueId),
|
||||
flagcx_id_wrapper.data(),
|
||||
flagcx_id_wrapper.size());
|
||||
}
|
||||
|
||||
VLOG(3) << "init FlagcxCommContext rank: " << rank << ", size: " << size
|
||||
<< ", unique_comm_key: " << unique_comm_key
|
||||
<< ", unique_key: " << unique_key << ", flagcx_id: "
|
||||
<< SerializeFlagcxUniqueId(*flagcx_handler->uniqueId);
|
||||
auto flagcx_comm_context =
|
||||
std::make_unique<FlagcxCommContext>(rank, size, flagcx_handler);
|
||||
// TODO(changtao): find a way to manage different device context,
|
||||
// now we use cuda device context as default
|
||||
comm_context_manager.SetStore(store);
|
||||
comm_context_manager.Emplace(unique_comm_key, std::move(flagcx_comm_context));
|
||||
}
|
||||
#endif
|
||||
|
||||
CommContext* CommContextManager::Emplace(
|
||||
const std::string& unique_comm_key,
|
||||
std::unique_ptr<CommContext> comm_context) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
id_to_comm_context_.find(unique_comm_key),
|
||||
id_to_comm_context_.end(),
|
||||
errors::AlreadyExists("The unique key %s already exists in the map.",
|
||||
unique_comm_key));
|
||||
id_to_comm_context_.emplace(unique_comm_key, std::move(comm_context));
|
||||
return id_to_comm_context_.at(unique_comm_key).get();
|
||||
}
|
||||
|
||||
CommContext* CommContextManager::Get(const std::string& unique_comm_key) const {
|
||||
PADDLE_ENFORCE_NE(
|
||||
id_to_comm_context_.find(unique_comm_key),
|
||||
id_to_comm_context_.end(),
|
||||
errors::NotFound("Can not find unique key %s in map.", unique_comm_key));
|
||||
|
||||
return id_to_comm_context_.at(unique_comm_key).get();
|
||||
}
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
int CommContextManager::GetRingId(const ncclComm_t& comm) const {
|
||||
for (const auto& iter : id_to_comm_context_) {
|
||||
if (static_cast<phi::distributed::NCCLCommContext*>(iter.second.get())
|
||||
->GetNcclComm() == comm) {
|
||||
return std::stoi(iter.first);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CommContextManager::Has(const std::string& unique_comm_key) const {
|
||||
return id_to_comm_context_.find(unique_comm_key) != id_to_comm_context_.end();
|
||||
}
|
||||
|
||||
void CommContextManager::SetGroupSize(const std::string& pg_key, int size) {
|
||||
pg_key_size_[pg_key] = size;
|
||||
}
|
||||
|
||||
void CommContextManager::AddGroupRanks(const std::string& pg_key,
|
||||
std::vector<int> global_ranks) {
|
||||
if (pg_key_ranks_.find(pg_key) == pg_key_ranks_.end()) {
|
||||
pg_key_ranks_[pg_key] = global_ranks;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> CommContextManager::GetGroupRanks(
|
||||
const std::string& pg_key) const {
|
||||
PADDLE_ENFORCE_NE(
|
||||
pg_key_ranks_.find(pg_key),
|
||||
pg_key_ranks_.end(),
|
||||
errors::NotFound("Can not find pg_key %d in GroupRanks.", pg_key));
|
||||
return pg_key_ranks_.at(pg_key);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/common/place.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/gpu/forwards.h"
|
||||
#include "paddle/phi/core/distributed/nccl_config.h"
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
#include <flagcx.h>
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
struct P2POption {
|
||||
bool is_p2p_op;
|
||||
int p2p_rank;
|
||||
int num_ranks;
|
||||
int rank;
|
||||
};
|
||||
|
||||
class Store;
|
||||
|
||||
class CommContextManager {
|
||||
public:
|
||||
CommContextManager() = default;
|
||||
~CommContextManager() = default;
|
||||
|
||||
static CommContextManager& GetInstance() {
|
||||
static CommContextManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void SetStore(const std::shared_ptr<Store>& store) { store_ = store; }
|
||||
|
||||
CommContext* Emplace(const std::string& unique_comm_key,
|
||||
std::unique_ptr<CommContext> comm_context);
|
||||
|
||||
PADDLE_API CommContext* Get(const std::string& unique_comm_key) const;
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
int GetRingId(const ncclComm_t& comm) const;
|
||||
#endif
|
||||
|
||||
PADDLE_API bool Has(const std::string& unique_comm_key) const;
|
||||
|
||||
PADDLE_API static void SetDeviceId(int dev_id);
|
||||
|
||||
PADDLE_API void SetGroupSize(const std::string& pg_key, int size);
|
||||
|
||||
PADDLE_API void AddGroupRanks(const std::string& pg_key,
|
||||
std::vector<int> global_ranks);
|
||||
|
||||
PADDLE_API std::vector<int> GetGroupRanks(const std::string& pg_key) const;
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
static void CreateNCCLCommContext(
|
||||
const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key = "",
|
||||
const P2POption* opt = nullptr,
|
||||
int nccl_comm_init_option = 0,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr = nullptr);
|
||||
static void RecreateNCCLComm(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
const std::string& hash_key = "",
|
||||
const P2POption* opt = nullptr);
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_GLOO)
|
||||
static void CreateGlooCommContext(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size);
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_CUSTOM_DEVICE
|
||||
static void CreateXCCLCommContext(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
const Place& place,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key = "");
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_XPU_BKCL)
|
||||
static void CreateBKCLCommContext(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key = "");
|
||||
#endif
|
||||
|
||||
#if defined(PADDLE_WITH_FLAGCX)
|
||||
static void CreateFlagcxCommContext(const std::shared_ptr<Store>& store,
|
||||
const std::string& unique_comm_key,
|
||||
int rank,
|
||||
int size,
|
||||
const std::string& hash_key = "");
|
||||
#endif
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(CommContextManager);
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<CommContext>>
|
||||
id_to_comm_context_;
|
||||
std::shared_ptr<Store> store_;
|
||||
static int device_id;
|
||||
|
||||
// process group key to global ranks map
|
||||
std::unordered_map<std::string, std::vector<int>> pg_key_ranks_;
|
||||
// process group key to group size map
|
||||
std::unordered_map<std::string, int> pg_key_size_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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 <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
#endif
|
||||
#if defined(PADDLE_WITH_NCCL)
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class Store;
|
||||
class CommTask {
|
||||
public:
|
||||
CommTask(const std::string& backend = "",
|
||||
const Place& place = Place(),
|
||||
const std::string& group_key = "",
|
||||
int rank = -1,
|
||||
int size = 0,
|
||||
int gid = 0,
|
||||
uint64_t seq = 0,
|
||||
int64_t numel = 0,
|
||||
ncclComm_t nccl_comm = nullptr,
|
||||
gpuStream_t nccl_stream = nullptr,
|
||||
CommType comm_type = CommType::UNKNOWN)
|
||||
: backend_(backend),
|
||||
place_(place),
|
||||
group_key_(group_key),
|
||||
rank_(rank),
|
||||
size_(size),
|
||||
gid_(gid),
|
||||
seq_(seq),
|
||||
numel_(numel),
|
||||
nccl_comm_(nccl_comm),
|
||||
nccl_stream_(nccl_stream),
|
||||
comm_type_(comm_type) {
|
||||
const char* global_rank = std::getenv("PADDLE_TRAINER_ID");
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
global_rank,
|
||||
common::errors::NotFound(
|
||||
"The environment variable 'PADDLE_TRAINER_ID' cannot be found."));
|
||||
global_rank_ = std::atoi(global_rank);
|
||||
}
|
||||
virtual ~CommTask() = default;
|
||||
|
||||
std::string UniqueKey() {
|
||||
return "group_key:" + group_key_ + ",op:" + CommTypeToString(comm_type_) +
|
||||
",gid:" + std::to_string(gid_) + ",seq:" + std::to_string(seq_);
|
||||
}
|
||||
|
||||
std::string GroupKey() { return group_key_; }
|
||||
std::string GetBackend() { return backend_; }
|
||||
Place GetPlace() { return place_; }
|
||||
int GetGlobalRank() { return global_rank_; }
|
||||
int GetRank() { return rank_; }
|
||||
int GetSize() { return size_; }
|
||||
int GetGid() { return gid_; }
|
||||
int64_t GetNumel() { return numel_; }
|
||||
uint64_t GetSeq() { return seq_; }
|
||||
CommType GetCommType() { return comm_type_; }
|
||||
bool GetTraceUpdated() { return start_trace_updated_; }
|
||||
void SetTraceUpdated() { start_trace_updated_ = true; }
|
||||
std::chrono::time_point<std::chrono::steady_clock> GetStartTime() {
|
||||
return start_time_;
|
||||
}
|
||||
std::shared_ptr<Store> GetStore() { return store_; }
|
||||
void SetStore(std::shared_ptr<Store> store) { store_ = store; }
|
||||
|
||||
ncclComm_t nccl_comm() { return nccl_comm_; }
|
||||
gpuStream_t nccl_stream() { return nccl_stream_; }
|
||||
|
||||
virtual std::string GetTraceMsg() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return "";
|
||||
}
|
||||
virtual void StartRecord() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return;
|
||||
}
|
||||
virtual void EndRecord() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
virtual void ClearRecord() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
virtual std::string GetCommErrors() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return "";
|
||||
}
|
||||
virtual bool IsStarted() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return false;
|
||||
}
|
||||
virtual bool IsTimeout() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return false;
|
||||
}
|
||||
virtual bool IsCompleted() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return false;
|
||||
}
|
||||
virtual void SetUpdated(bool updated) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return;
|
||||
}
|
||||
virtual bool IsUpdated() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return false;
|
||||
}
|
||||
virtual void AbortComm() {
|
||||
PADDLE_THROW(
|
||||
common::errors::Unimplemented("%s is not implemented.", __func__));
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string backend_;
|
||||
Place place_;
|
||||
std::string group_key_;
|
||||
int global_rank_;
|
||||
int rank_;
|
||||
int size_;
|
||||
int gid_;
|
||||
uint64_t seq_{0};
|
||||
int64_t numel_;
|
||||
ncclComm_t nccl_comm_;
|
||||
gpuStream_t nccl_stream_;
|
||||
CommType comm_type_;
|
||||
bool start_trace_updated_{false};
|
||||
|
||||
// task status
|
||||
bool started_ = false;
|
||||
bool completed_ = false;
|
||||
// task status changed
|
||||
bool updated_ = true;
|
||||
bool aborted_{false};
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
std::shared_ptr<Store> store_;
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(CommTask);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,278 @@
|
||||
// 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.
|
||||
|
||||
#if defined(PADDLE_WITH_GLOO)
|
||||
#include <gloo/rendezvous/prefix_store.h>
|
||||
|
||||
#include "paddle/phi/core/distributed/gloo_comm_context.h"
|
||||
#include "paddle/phi/core/distributed/gloo_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/gloo_store.h"
|
||||
#endif
|
||||
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
#include "glog/logging.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/core/distributed/store/store.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/core/distributed/comm_task_manager.h"
|
||||
#include "paddle/phi/core/distributed/nccl_comm_context.h"
|
||||
#endif
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
std::thread CommTaskManager::comm_task_loop_thread_;
|
||||
std::thread CommTaskManager::comm_task_clear_loop_thread_;
|
||||
const int64_t CommTaskManager::loop_thread_sleep_millis = 10000;
|
||||
|
||||
std::atomic<bool> CommTaskManager::terminated_;
|
||||
std::mutex CommTaskManager::comm_task_list_mutex_;
|
||||
std::condition_variable CommTaskManager::comm_task_list_cv_;
|
||||
std::list<std::shared_ptr<CommTask>> CommTaskManager::comm_task_list_;
|
||||
|
||||
std::mutex CommTaskManager::comm_task_clear_list_mutex_;
|
||||
std::condition_variable CommTaskManager::comm_task_clear_list_cv_;
|
||||
std::list<std::shared_ptr<CommTask>> CommTaskManager::comm_task_clear_list_;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
CommTaskManager::init_comm_task_map_;
|
||||
std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
CommTaskManager::start_comm_task_map_;
|
||||
std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
CommTaskManager::group_last_comm_task_;
|
||||
std::chrono::time_point<std::chrono::steady_clock>
|
||||
CommTaskManager::last_update_time_ = std::chrono::steady_clock::now();
|
||||
|
||||
CommTaskManager::CommTaskManager() : timeout_(0) {
|
||||
terminated_.store(false);
|
||||
comm_task_loop_thread_ = std::thread(&CommTaskManager::CommTaskLoop, this);
|
||||
comm_task_clear_loop_thread_ =
|
||||
std::thread(&CommTaskManager::CommTaskClearLoop, this);
|
||||
LOG(INFO) << "CommTaskManager init success.";
|
||||
}
|
||||
CommTaskManager::~CommTaskManager() {
|
||||
terminated_.store(true);
|
||||
|
||||
if (comm_task_loop_thread_.joinable()) {
|
||||
comm_task_list_cv_.notify_one();
|
||||
comm_task_loop_thread_.join();
|
||||
}
|
||||
|
||||
if (comm_task_clear_loop_thread_.joinable()) {
|
||||
comm_task_clear_list_cv_.notify_one();
|
||||
comm_task_clear_loop_thread_.join();
|
||||
}
|
||||
LOG(INFO) << "CommTaskManager destruct success.";
|
||||
}
|
||||
|
||||
void CommTaskManager::CommTaskEnqueue(std::shared_ptr<CommTask> comm_task) {
|
||||
if (!terminated_.load()) {
|
||||
std::lock_guard<std::mutex> lock(comm_task_list_mutex_);
|
||||
comm_task_list_.emplace_back(std::move(comm_task));
|
||||
}
|
||||
}
|
||||
|
||||
void CommTaskManager::CommTaskClearEnqueue(
|
||||
std::shared_ptr<CommTask> comm_task) {
|
||||
if (!terminated_.load()) {
|
||||
std::lock_guard<std::mutex> lock(comm_task_clear_list_mutex_);
|
||||
comm_task_clear_list_.emplace_back(comm_task);
|
||||
}
|
||||
}
|
||||
|
||||
void CommTaskManager::Stop() {
|
||||
terminated_.store(true);
|
||||
|
||||
LOG(INFO) << "CommTaskManager stopped begin.";
|
||||
if (comm_task_loop_thread_.joinable()) {
|
||||
comm_task_list_cv_.notify_one();
|
||||
comm_task_loop_thread_.join();
|
||||
}
|
||||
|
||||
if (comm_task_clear_loop_thread_.joinable()) {
|
||||
comm_task_clear_list_cv_.notify_one();
|
||||
comm_task_clear_loop_thread_.join();
|
||||
}
|
||||
|
||||
LOG(INFO) << "CommTaskManager stopped.";
|
||||
}
|
||||
|
||||
inline void LogLongStr(const std::string prefix, const std::string& log) {
|
||||
size_t max_log_size = 20000;
|
||||
if (log.size() >= max_log_size) {
|
||||
int log_count = log.size() / max_log_size + 1;
|
||||
int index = 0;
|
||||
int part = 0;
|
||||
while (index + max_log_size < log.size()) {
|
||||
LOG(INFO) << prefix << "part:" << part << "/" << log_count << ","
|
||||
<< log.substr(index, max_log_size) << std::endl;
|
||||
index += max_log_size;
|
||||
part++;
|
||||
}
|
||||
LOG(INFO) << prefix << "part:" << part << "/" << log_count << ","
|
||||
<< log.substr(index) << std::endl;
|
||||
} else {
|
||||
LOG(INFO) << prefix << "part:0/1," << log << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void CommTaskManager::CommTaskLoop() {
|
||||
bool done = false;
|
||||
while (!terminated_.load() || !done) {
|
||||
std::unique_lock<std::mutex> lock(comm_task_list_mutex_);
|
||||
VLOG(3) << "IsTimeout: " << IsTimeout()
|
||||
<< ", comm_task_list_ size: " << comm_task_list_.size()
|
||||
<< ", init_comm_task_map_ size: " << init_comm_task_map_.size()
|
||||
<< ", start_comm_task_map_ size: " << start_comm_task_map_.size()
|
||||
<< ", logged_ " << logged_;
|
||||
|
||||
comm_task_list_cv_.wait_for(
|
||||
lock,
|
||||
std::chrono::milliseconds(loop_thread_sleep_millis),
|
||||
[&]() -> bool { return terminated_.load(); });
|
||||
|
||||
if (IsTimeout() && !logged_) {
|
||||
// case 1: all group is empty, has no task
|
||||
// report error immediately
|
||||
if (group_last_comm_task_.empty()) {
|
||||
LOG(WARNING) << "Find no task started in all group";
|
||||
} else {
|
||||
// case 2: all group is not empty, but all last task is completed
|
||||
// case 3: all group is not empty, some group task started but not
|
||||
for (const auto& iter : group_last_comm_task_) {
|
||||
LogLongStr("Find last group comm task:", iter.second->GetTraceMsg());
|
||||
}
|
||||
}
|
||||
logged_ = true;
|
||||
}
|
||||
for (auto iter = comm_task_list_.begin(); iter != comm_task_list_.end();) {
|
||||
auto task = *iter;
|
||||
if (task->IsTimeout()) {
|
||||
if (!task->IsStarted()) {
|
||||
LOG(WARNING) << "Find timeout init but not start task:"
|
||||
<< task->GetTraceMsg();
|
||||
std::string task_key = task->UniqueKey();
|
||||
init_comm_task_map_[task_key] = task;
|
||||
} else if (!task->IsCompleted()) {
|
||||
LOG(WARNING) << "Find timeout start but not finish task:"
|
||||
<< task->GetTraceMsg();
|
||||
std::string task_key = task->UniqueKey();
|
||||
start_comm_task_map_[task_key] = task;
|
||||
}
|
||||
iter = comm_task_list_.erase(iter);
|
||||
} else {
|
||||
if (task->IsStarted()) {
|
||||
if (task->IsCompleted()) {
|
||||
CommTaskClearEnqueue(task);
|
||||
iter = comm_task_list_.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
UpdateLastCommTask(task);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = init_comm_task_map_.begin();
|
||||
iter != init_comm_task_map_.end();) {
|
||||
auto task = iter->second;
|
||||
if (task->IsStarted()) {
|
||||
std::string task_key = task->UniqueKey();
|
||||
start_comm_task_map_[task_key] = task;
|
||||
iter = init_comm_task_map_.erase(iter);
|
||||
LOG(INFO) << "Start timeout task: " << task->GetTraceMsg();
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = start_comm_task_map_.begin();
|
||||
iter != start_comm_task_map_.end();) {
|
||||
auto task = iter->second;
|
||||
if (task->IsCompleted()) {
|
||||
CommTaskClearEnqueue(task);
|
||||
UpdateLastCommTask(task);
|
||||
iter = start_comm_task_map_.erase(iter);
|
||||
LOG(INFO) << "Finish timeout task: " << task->GetTraceMsg();
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (comm_task_list_.empty() && init_comm_task_map_.empty() &&
|
||||
start_comm_task_map_.empty()) {
|
||||
done = true;
|
||||
} else {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommTaskManager::CommTaskClearLoop() {
|
||||
std::future<void> future;
|
||||
while (!terminated_.load()) {
|
||||
if (future.valid()) {
|
||||
future.wait();
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(comm_task_clear_list_mutex_);
|
||||
comm_task_clear_list_cv_.wait_for(
|
||||
lock,
|
||||
std::chrono::milliseconds(loop_thread_sleep_millis),
|
||||
[&]() -> bool { return terminated_.load(); });
|
||||
|
||||
VLOG(3) << "comm_task_clear_list_ size: " << comm_task_clear_list_.size();
|
||||
for (auto iter = comm_task_clear_list_.begin();
|
||||
iter != comm_task_clear_list_.end();) {
|
||||
auto task = *iter;
|
||||
VLOG(3) << "start clear task: " << task->GetTraceMsg();
|
||||
future = std::async(std::launch::async, [&]() { task->ClearRecord(); });
|
||||
if (future.wait_for(std::chrono::seconds(30)) ==
|
||||
std::future_status::timeout) {
|
||||
VLOG(0) << "clear task timeout, detail: " << task->GetTraceMsg();
|
||||
break;
|
||||
}
|
||||
VLOG(3) << "end clear task: " << task->GetTraceMsg();
|
||||
iter = comm_task_clear_list_.erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommTaskManager::UpdateLastCommTask(std::shared_ptr<CommTask> task) {
|
||||
if (!task->IsUpdated()) {
|
||||
return;
|
||||
}
|
||||
group_last_comm_task_[task->GroupKey()] = task;
|
||||
last_update_time_ = std::chrono::steady_clock::now();
|
||||
task->SetUpdated(false);
|
||||
}
|
||||
|
||||
void CommTaskManager::SetTimeout(int64_t timeout) {
|
||||
timeout_ = std::chrono::milliseconds(timeout);
|
||||
}
|
||||
|
||||
bool CommTaskManager::IsTimeout() {
|
||||
auto current_timepoint = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
current_timepoint - last_update_time_) >= timeout_;
|
||||
}
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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 <atomic>
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
#include "paddle/phi/core/distributed/comm_task.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
enum ErrorHandlingMode { NoHandling = 0, TearDown = 1 };
|
||||
|
||||
class Store;
|
||||
|
||||
class CommTaskManager {
|
||||
public:
|
||||
CommTaskManager();
|
||||
~CommTaskManager();
|
||||
|
||||
public:
|
||||
static CommTaskManager& GetInstance() {
|
||||
static CommTaskManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void CommTaskEnqueue(std::shared_ptr<CommTask> comm_task);
|
||||
void CommTaskClearEnqueue(std::shared_ptr<CommTask> comm_task);
|
||||
void Stop();
|
||||
void UpdateLastCommTask(std::shared_ptr<CommTask> comm_task);
|
||||
void SetTimeout(int64_t timeout);
|
||||
|
||||
private:
|
||||
void CommTaskLoop();
|
||||
void CommTaskClearLoop();
|
||||
bool IsTimeout();
|
||||
|
||||
static std::thread comm_task_loop_thread_;
|
||||
static std::thread comm_task_clear_loop_thread_;
|
||||
static const int64_t loop_thread_sleep_millis;
|
||||
|
||||
static std::atomic<bool> terminated_;
|
||||
|
||||
static std::mutex comm_task_list_mutex_;
|
||||
static std::condition_variable comm_task_list_cv_;
|
||||
static std::list<std::shared_ptr<CommTask>> comm_task_list_;
|
||||
|
||||
static std::mutex comm_task_clear_list_mutex_;
|
||||
static std::condition_variable comm_task_clear_list_cv_;
|
||||
static std::list<std::shared_ptr<CommTask>> comm_task_clear_list_;
|
||||
|
||||
// not start task
|
||||
static std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
init_comm_task_map_;
|
||||
// start but not finish task
|
||||
static std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
start_comm_task_map_;
|
||||
std::shared_ptr<Store> store_;
|
||||
// record last comm task in current group, eg: group_key->comm_task
|
||||
static std::unordered_map<std::string, std::shared_ptr<CommTask>>
|
||||
group_last_comm_task_;
|
||||
static std::chrono::time_point<std::chrono::steady_clock> last_update_time_;
|
||||
std::chrono::milliseconds timeout_;
|
||||
bool logged_ = false;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2025 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/flagcx_comm_context.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
// #include "paddle/phi/core/distributed/check/nccl_dynamic_check.h"
|
||||
#include "paddle/phi/core/distributed/check/static_check.h"
|
||||
#include "paddle/phi/core/distributed/flagcx_tools.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
#ifdef PADDLE_WITH_FLAGCX
|
||||
#include <flagcx.h>
|
||||
#endif
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
// set this flag to `true` and recompile to enable dynamic checks
|
||||
// constexpr bool FLAGS_enable_flagcx_dynamic_check = false;
|
||||
|
||||
FlagcxCommContext::FlagcxCommContext(int rank,
|
||||
int size,
|
||||
flagcxHandlerGroup_t flagcx_handler)
|
||||
: CommContext(rank, size),
|
||||
flagcx_version_(0),
|
||||
flagcx_handler_(flagcx_handler) {
|
||||
phi::dynload::flagcxCommInitRank(
|
||||
&flagcx_handler_->comm, size_, flagcx_handler_->uniqueId, rank_),
|
||||
phi::dynload::flagcxGetVersion(&flagcx_version_);
|
||||
}
|
||||
|
||||
int FlagcxCommContext::GetFlagcxVersion() { return flagcx_version_; }
|
||||
|
||||
flagcxComm_t FlagcxCommContext::GetFlagcxComm() {
|
||||
return flagcx_handler_->comm;
|
||||
}
|
||||
|
||||
void FlagcxCommContext::Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
flagcxStream_t stream) {
|
||||
CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(phi::dynload::flagcxBroadcast(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
root,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
|
||||
void FlagcxCommContext::AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::GatherLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(phi::dynload::flagcxAllGather(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
void FlagcxCommContext::ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::ScatterLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(
|
||||
phi::dynload::flagcxReduceScatter(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
out_tensor->numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
|
||||
void FlagcxCommContext::Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(in_tensor, rank_, size_);
|
||||
|
||||
FLAGCX_CHECK(phi::dynload::flagcxSend(in_tensor.data(),
|
||||
count,
|
||||
ToFlagcxDataType(in_tensor.dtype()),
|
||||
peer,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
VLOG(3) << "rank " << GetRank() << " send "
|
||||
<< common::product(in_tensor.dims()) << " to " << peer;
|
||||
}
|
||||
|
||||
void FlagcxCommContext::Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(*out_tensor, rank_, size_);
|
||||
|
||||
FLAGCX_CHECK(phi::dynload::flagcxRecv(out_tensor->data(),
|
||||
count,
|
||||
ToFlagcxDataType(out_tensor->dtype()),
|
||||
peer,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
VLOG(3) << "rank " << GetRank() << " recv "
|
||||
<< common::product(out_tensor->dims()) << " from " << peer;
|
||||
}
|
||||
|
||||
void FlagcxCommContext::AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(phi::dynload::flagcxAllReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
|
||||
void FlagcxCommContext::Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
int root,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ root,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(phi::dynload::flagcxReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
root,
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
|
||||
void FlagcxCommContext::AllToAll(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
FLAGCX_CHECK(phi::dynload::flagcxAlltoAll(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel() / size_,
|
||||
ToFlagcxDataType(in_tensor.type()),
|
||||
flagcx_handler_->comm,
|
||||
stream));
|
||||
}
|
||||
|
||||
void FlagcxCommContext::GroupStart() {
|
||||
FLAGCX_CHECK(phi::dynload::flagcxGroupStart(flagcx_handler_->comm));
|
||||
}
|
||||
void FlagcxCommContext::GroupEnd() {
|
||||
FLAGCX_CHECK(phi::dynload::flagcxGroupEnd(flagcx_handler_->comm));
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2025 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/backends/dynload/flagcx.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
|
||||
namespace phi {
|
||||
class DenseTensor;
|
||||
namespace distributed {
|
||||
|
||||
class FlagcxCommContext final : public CommContext {
|
||||
public:
|
||||
FlagcxCommContext(int rank, int size, flagcxHandlerGroup_t flagcx_handler);
|
||||
~FlagcxCommContext() override = default;
|
||||
|
||||
int GetFlagcxVersion();
|
||||
|
||||
flagcxComm_t GetFlagcxComm();
|
||||
|
||||
void Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxRedOp_t reduce_type,
|
||||
int root,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void AllToAll(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
flagcxStream_t stream);
|
||||
|
||||
void GroupStart();
|
||||
|
||||
void GroupEnd();
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(FlagcxCommContext);
|
||||
|
||||
int flagcx_version_;
|
||||
|
||||
std::unique_ptr<phi::GPUContext> dev_ctx_;
|
||||
|
||||
// used for comm wait compute, compute_stream-->event-->comm_stream
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type> compute_event_;
|
||||
|
||||
// used for compute wait comm, comm_stream-->event-->compute_stream
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type> comm_event_;
|
||||
|
||||
public:
|
||||
flagcxHandlerGroup_t flagcx_handler_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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/flagcx_tools.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
flagcxRedOp_t ToFlagcxRedType(ReduceOp reduction) {
|
||||
static const std::unordered_map<ReduceOp, flagcxRedOp_t> red_type = {
|
||||
{ReduceOp::MIN, flagcxMin},
|
||||
{ReduceOp::MAX, flagcxMax},
|
||||
{ReduceOp::SUM, flagcxSum},
|
||||
{ReduceOp::PRODUCT, flagcxProd},
|
||||
{ReduceOp::AVG, flagcxAvg},
|
||||
};
|
||||
auto it = red_type.find(reduction);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
it != red_type.end(),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"Invalid flagcx reduction. Must be flagcxMin | flagcxMax | "
|
||||
"flagcxProd | flagcxSum | flagcxAvg."));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string SerializeFlagcxUniqueId(const flagcxUniqueId& flagcxID) {
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&flagcxID);
|
||||
std::ostringstream oss;
|
||||
for (auto i = 0; i < FLAGCX_UNIQUE_ID_BYTES; ++i) {
|
||||
oss << std::hex << static_cast<int>(bytes[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string FlagcxDTypeToString(flagcxDataType_t dtype) {
|
||||
#define PD_FLAGCX_DTYPE_TO_STR(__flagcx_dtype, __str_dtype) \
|
||||
if (dtype == __flagcx_dtype) return __str_dtype;
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxFloat, "float32");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxFloat32, "float32");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxHalf, "float16");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxFloat16, "float16");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxBfloat16, "bfloat16");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxDouble, "float64");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxFloat64, "float64");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxInt8, "int8");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxChar, "int8");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxUint8, "uint8");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxInt32, "int32");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxInt, "int32");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxUint32, "uint32");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxInt64, "int64");
|
||||
PD_FLAGCX_DTYPE_TO_STR(flagcxUint64, "uint64");
|
||||
|
||||
#undef PD_FLAGCX_DTYPE_TO_STR
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"This datatype %d in flagcx is not supported.", static_cast<int>(dtype)));
|
||||
}
|
||||
|
||||
std::string FlagcxRedTypeToString(flagcxRedOp_t op) {
|
||||
if (op == flagcxSum) return "SUM";
|
||||
if (op == flagcxProd) return "PROD";
|
||||
if (op == flagcxMin) return "MIN";
|
||||
if (op == flagcxMax) return "MAX";
|
||||
if (op == flagcxAvg) return "AVG";
|
||||
return "UDF_" + std::to_string(op);
|
||||
}
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 <flagcx.h>
|
||||
#include <string>
|
||||
#include "paddle/phi/core/distributed/types.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
#define FLAGCX_CHECK(cmd) \
|
||||
do { \
|
||||
flagcxResult_t r = cmd; \
|
||||
if (r != flagcxSuccess) { \
|
||||
PADDLE_THROW( \
|
||||
common::errors::External("Failed, FlagCX error %s:%d '%s'\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
phi::dynload::flagcxGetErrorString(r))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
flagcxRedOp_t ToFlagcxRedType(ReduceOp reduction);
|
||||
|
||||
std::string SerializeFlagcxUniqueId(const flagcxUniqueId& flagcxID);
|
||||
|
||||
std::string FlagcxDTypeToString(flagcxDataType_t dtype);
|
||||
|
||||
std::string FlagcxRedTypeToString(flagcxRedOp_t op);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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/gloo_comm_context.h"
|
||||
#include "paddle/phi/core/distributed/gloo_utils.h"
|
||||
|
||||
#include <gloo/allgather.h>
|
||||
#include <gloo/allreduce.h>
|
||||
#include <gloo/barrier.h>
|
||||
#include <gloo/broadcast.h>
|
||||
#include <gloo/gather.h>
|
||||
#include <gloo/reduce.h>
|
||||
#include <gloo/scatter.h>
|
||||
#include <gloo/types.h>
|
||||
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/distributed/check/static_check.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
GlooCommContext::GlooCommContext(
|
||||
int rank,
|
||||
int size,
|
||||
std::shared_ptr<gloo::rendezvous::Store> store,
|
||||
std::shared_ptr<gloo::transport::Device> device)
|
||||
: CommContext(rank, size) {
|
||||
gloo_context_ = std::make_shared<gloo::rendezvous::Context>(rank, size);
|
||||
gloo_context_->connectFullMesh(*store, device);
|
||||
}
|
||||
|
||||
void GlooCommContext::Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
uint32_t tag) {
|
||||
// gloo only uses CPU now
|
||||
CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_,
|
||||
AllocationType::CPU);
|
||||
gloo::BroadcastOptions opts(gloo_context_);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
if (rank_ == root) {
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
}
|
||||
opts.setRoot(root);
|
||||
opts.setTag(tag);
|
||||
gloo::broadcast(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
uint32_t tag) {
|
||||
// gloo only uses CPU now
|
||||
|
||||
gloo::AllgatherOptions opts(gloo_context_);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
opts.setTag(tag);
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
gloo::allgather(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int reduce_type,
|
||||
uint32_t tag) {
|
||||
gloo::AllreduceOptions opts(gloo_context_);
|
||||
opts.setTag(tag);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
GENERATE_FUNC(dtype, SetReduceFunc, &opts, reduce_type);
|
||||
gloo::allreduce(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int reduce_type,
|
||||
int root,
|
||||
uint32_t tag) {
|
||||
gloo::ReduceOptions opts(gloo_context_);
|
||||
opts.setRoot(root);
|
||||
opts.setTag(tag);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
GENERATE_FUNC(dtype, SetReduceFunc, &opts, reduce_type);
|
||||
gloo::reduce(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Gather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int src,
|
||||
uint32_t tag) {
|
||||
gloo::GatherOptions opts(gloo_context_);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
opts.setTag(tag);
|
||||
opts.setRoot(src);
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
if (rank_ == src) {
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
}
|
||||
gloo::gather(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Scatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int src,
|
||||
int size,
|
||||
uint32_t tag) {
|
||||
gloo::ScatterOptions opts(gloo_context_);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
if (rank_ == src) {
|
||||
if (size == 0) {
|
||||
size = size_;
|
||||
}
|
||||
GENERATE_FUNC(dtype, SetInputForScatter, &opts, in_tensor, size);
|
||||
}
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
opts.setRoot(src);
|
||||
opts.setTag(tag);
|
||||
gloo::scatter(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Barrier() {
|
||||
gloo::BarrierOptions opts(gloo_context_);
|
||||
gloo::barrier(opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Send(const DenseTensor& in_tensor,
|
||||
int dst,
|
||||
uint32_t tag) {
|
||||
SendRecvOptions opts(gloo_context_);
|
||||
const auto& dtype = in_tensor.dtype();
|
||||
GENERATE_FUNC(dtype, SetInput, &opts, in_tensor);
|
||||
opts.setSrc(gloo_context_->rank);
|
||||
opts.setDst(dst);
|
||||
opts.setTag(tag);
|
||||
send_recv(&opts);
|
||||
}
|
||||
|
||||
void GlooCommContext::Recv(DenseTensor* out_tensor, int src, uint32_t tag) {
|
||||
SendRecvOptions opts(gloo_context_);
|
||||
const auto& dtype = out_tensor->dtype();
|
||||
GENERATE_FUNC(dtype, SetOutput, &opts, out_tensor);
|
||||
opts.setSrc(src);
|
||||
opts.setDst(gloo_context_->rank);
|
||||
opts.setTag(tag);
|
||||
send_recv(&opts);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 <gloo/rendezvous/context.h>
|
||||
#include <gloo/rendezvous/store.h>
|
||||
#include <gloo/transport/tcp/device.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
|
||||
namespace phi {
|
||||
class DenseTensor;
|
||||
namespace distributed {
|
||||
|
||||
class GlooCommContext final : public CommContext {
|
||||
public:
|
||||
GlooCommContext(int rank,
|
||||
int size,
|
||||
std::shared_ptr<gloo::rendezvous::Store> store,
|
||||
std::shared_ptr<gloo::transport::Device> device);
|
||||
|
||||
void Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int reduce_type,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int reduce_type,
|
||||
int root,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void Gather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int src,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void Scatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int src,
|
||||
int size = 0,
|
||||
uint32_t tag = 0);
|
||||
|
||||
void Barrier();
|
||||
|
||||
void Send(const DenseTensor& in_tensor, int dst, uint32_t tag = 0);
|
||||
|
||||
void Recv(DenseTensor* out_tensor, int src, uint32_t tag = 0);
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(GlooCommContext);
|
||||
|
||||
std::shared_ptr<gloo::rendezvous::Context> gloo_context_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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.
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <gloo/common/win.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/distributed/gloo_utils.h"
|
||||
#include "paddle/phi/core/distributed/store/tcp_utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
std::shared_ptr<gloo::transport::Device> CreateDeviceForInterface(
|
||||
const std::string& ifname) {
|
||||
gloo::transport::tcp::attr attr;
|
||||
attr.iface = ifname;
|
||||
return gloo::transport::tcp::CreateDevice(attr);
|
||||
}
|
||||
|
||||
std::shared_ptr<gloo::transport::Device> CreateDeviceForHostname(
|
||||
const std::string& hostname) {
|
||||
gloo::transport::tcp::attr attr;
|
||||
attr.hostname = hostname;
|
||||
return gloo::transport::tcp::CreateDevice(attr);
|
||||
}
|
||||
|
||||
std::shared_ptr<gloo::transport::Device> CreateDefaultDevice() {
|
||||
std::array<char, HOST_NAME_MAX> hostname = {};
|
||||
auto ret = ::gethostname(hostname.data(), HOST_NAME_MAX);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
ret,
|
||||
0,
|
||||
common::errors::Fatal("Get hostname error for createDefaultDevice."));
|
||||
::addrinfo* result;
|
||||
result = phi::distributed::tcputils::get_addr_info(
|
||||
hostname.data(), "", 0, AF_UNSPEC);
|
||||
::addrinfo* cur;
|
||||
for (cur = result; cur != nullptr; cur = cur->ai_next) {
|
||||
phi::distributed::SocketType socket =
|
||||
::socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
|
||||
if (socket == -1) {
|
||||
continue;
|
||||
}
|
||||
ret = ::bind(socket, cur->ai_addr, cur->ai_addrlen);
|
||||
#ifdef _WIN32
|
||||
closesocket(socket);
|
||||
#else
|
||||
close(socket);
|
||||
#endif
|
||||
if (ret == -1) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (cur != nullptr) {
|
||||
return CreateDeviceForHostname(hostname.data());
|
||||
}
|
||||
return CreateDeviceForHostname("127.0.0.1");
|
||||
}
|
||||
|
||||
std::shared_ptr<gloo::transport::Device> CreateGlooDevice() {
|
||||
char* ifname = std::getenv("GLOO_SOCKET_IFNAME");
|
||||
if (ifname && std::strlen(ifname) > 1) {
|
||||
return CreateDeviceForInterface(std::string(ifname));
|
||||
} else {
|
||||
return CreateDefaultDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void send_recv(SendRecvOptions* opts) {
|
||||
const auto& context = opts->context;
|
||||
gloo::transport::UnboundBuffer* in = opts->in.get();
|
||||
gloo::transport::UnboundBuffer* out = opts->out.get();
|
||||
const auto slot = gloo::Slot::build(kSendRecvSlotPrefix, opts->tag);
|
||||
|
||||
if (context->rank == opts->src) {
|
||||
in->send(opts->dst, slot);
|
||||
in->waitSend(opts->timeout);
|
||||
} else if (context->rank == opts->dst) {
|
||||
out->recv(opts->src, slot);
|
||||
out->waitRecv(opts->timeout);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,219 @@
|
||||
// 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 <gloo/allreduce.h>
|
||||
#include <gloo/math.h>
|
||||
#include <gloo/transport/tcp/device.h>
|
||||
#include <gloo/types.h>
|
||||
|
||||
#include <climits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/common/data_type.h"
|
||||
#include "paddle/phi/common/reduce_type.h"
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
// data preparation
|
||||
#ifdef _WIN32
|
||||
#define GENERATE_FUNC(type, func, ...) \
|
||||
switch (type) { \
|
||||
case DataType::FLOAT32: \
|
||||
func<float>(__VA_ARGS__); \
|
||||
break; \
|
||||
case DataType::FLOAT64: \
|
||||
func<double>(__VA_ARGS__); \
|
||||
break; \
|
||||
case DataType::FLOAT16: \
|
||||
func<gloo::float16>(__VA_ARGS__); \
|
||||
break; \
|
||||
case DataType::INT32: \
|
||||
func<int32_t>(__VA_ARGS__); \
|
||||
break; \
|
||||
case DataType::INT64: \
|
||||
func<int64_t>(__VA_ARGS__); \
|
||||
break; \
|
||||
default: \
|
||||
VLOG(0) << "Error: Unknown DataType."; \
|
||||
exit(-1); \
|
||||
}
|
||||
#define HOST_NAME_MAX 256
|
||||
#else
|
||||
#define GENERATE_FUNC(type, func, args...) \
|
||||
switch (type) { \
|
||||
case DataType::FLOAT32: \
|
||||
func<float>(args); \
|
||||
break; \
|
||||
case DataType::FLOAT64: \
|
||||
func<double>(args); \
|
||||
break; \
|
||||
case DataType::FLOAT16: \
|
||||
func<gloo::float16>(args); \
|
||||
break; \
|
||||
case DataType::INT32: \
|
||||
func<int32_t>(args); \
|
||||
break; \
|
||||
case DataType::INT64: \
|
||||
func<int64_t>(args); \
|
||||
break; \
|
||||
case DataType::INT8: \
|
||||
func<int8_t>(args); \
|
||||
break; \
|
||||
case DataType::UINT8: \
|
||||
func<uint8_t>(args); \
|
||||
break; \
|
||||
case DataType::BOOL: \
|
||||
func<bool>(args); \
|
||||
break; \
|
||||
case DataType::BFLOAT16: \
|
||||
func<phi::dtype::bfloat16>(args); \
|
||||
break; \
|
||||
default: \
|
||||
VLOG(0) << "Error: Unknown DataType."; \
|
||||
exit(-1); \
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename P>
|
||||
void SetOutput(P* opts, DenseTensor* tensor) {
|
||||
opts->setOutput(reinterpret_cast<T*>(tensor->data()), tensor->numel());
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
void SetInput(P* opts, const DenseTensor& tensor) {
|
||||
// gloo only support mutable data input
|
||||
opts->setInput(reinterpret_cast<T*>(const_cast<void*>(tensor.data())),
|
||||
tensor.numel());
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
void SetInputForScatter(P* opts, const DenseTensor& tensor, int nranks) {
|
||||
std::vector<T*> ret;
|
||||
ret.reserve(nranks);
|
||||
T* raw_pointer = reinterpret_cast<T*>(const_cast<void*>(tensor.data()));
|
||||
size_t offset = 0;
|
||||
for (int i = 0; i < nranks; i++) {
|
||||
ret.push_back(raw_pointer + offset);
|
||||
offset += tensor.numel() / nranks;
|
||||
}
|
||||
opts->setInputs(ret, tensor.numel() / nranks);
|
||||
}
|
||||
|
||||
template <typename T, typename P>
|
||||
void SetReduceFunc(P* opts, int reduce_type) {
|
||||
// gloo only support mutable data input
|
||||
ReduceType reduce_type_enum = static_cast<ReduceType>(reduce_type);
|
||||
switch (reduce_type_enum) {
|
||||
case ReduceType::kRedSum:
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::sum<T>));
|
||||
break;
|
||||
case ReduceType::kRedMax:
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::max<T>));
|
||||
break;
|
||||
case ReduceType::kRedMin:
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::min<T>));
|
||||
break;
|
||||
case ReduceType::kRedProd:
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::product<T>));
|
||||
break;
|
||||
case ReduceType::kRedAll:
|
||||
// NOTE(zhonghui): There is no reduce_all math function for gloo, just use
|
||||
// min to replace
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::min<T>));
|
||||
break;
|
||||
case ReduceType::kRedAny:
|
||||
// NOTE(ooooo): There is no reduce_any math function for gloo, just use
|
||||
// max to replace
|
||||
opts->setReduceFunction(
|
||||
static_cast<void (*)(void*, const void*, const void*, size_t)>(
|
||||
&gloo::max<T>));
|
||||
break;
|
||||
default:
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Unsupported reduce type: %d.", reduce_type));
|
||||
}
|
||||
}
|
||||
|
||||
// env preparation
|
||||
std::shared_ptr<gloo::transport::Device> CreateGlooDevice();
|
||||
|
||||
constexpr uint8_t kSendRecvSlotPrefix = 0x08;
|
||||
|
||||
class SendRecvOptions {
|
||||
public:
|
||||
explicit SendRecvOptions(const std::shared_ptr<gloo::Context>& context)
|
||||
: context(context), timeout(context->getTimeout()) {}
|
||||
|
||||
template <typename T>
|
||||
void setInput(T* ptr, size_t elements) {
|
||||
this->in = context->createUnboundBuffer(ptr, elements * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setOutput(T* ptr, size_t elements) {
|
||||
this->out = context->createUnboundBuffer(ptr, elements * sizeof(T));
|
||||
}
|
||||
|
||||
void setSrc(int src) { this->src = src; }
|
||||
|
||||
void setDst(int dst) { this->dst = dst; }
|
||||
|
||||
void setTag(uint32_t tag) { this->tag = tag; }
|
||||
|
||||
void setTimeout(std::chrono::milliseconds timeout) {
|
||||
this->timeout = timeout;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<gloo::Context> context;
|
||||
std::unique_ptr<gloo::transport::UnboundBuffer> in;
|
||||
std::unique_ptr<gloo::transport::UnboundBuffer> out;
|
||||
|
||||
// Rank of process to send_recv from.
|
||||
int src = -1;
|
||||
|
||||
// Rank of process to send_recv to.
|
||||
int dst = -1;
|
||||
|
||||
// Tag for this operation.
|
||||
// Must be unique across operations executing in parallel.
|
||||
uint32_t tag = 0;
|
||||
|
||||
// End-to-end timeout for this operation.
|
||||
std::chrono::milliseconds timeout;
|
||||
|
||||
friend void send_recv(SendRecvOptions*);
|
||||
};
|
||||
|
||||
void send_recv(SendRecvOptions* opts);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,308 @@
|
||||
// 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/nccl_comm_context.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/dense_tensor.h"
|
||||
#include "paddle/phi/core/distributed/check/nccl_dynamic_check.h"
|
||||
#include "paddle/phi/core/distributed/check/static_check.h"
|
||||
#include "paddle/phi/core/distributed/nccl_tools.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
// set this flag to `true` and recompile to enable dynamic checks
|
||||
constexpr bool FLAGS_enable_nccl_dynamic_check = false;
|
||||
|
||||
NCCLCommContext::NCCLCommContext(
|
||||
int rank,
|
||||
int size,
|
||||
ncclUniqueId nccl_id,
|
||||
int nccl_comm_init_option,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr)
|
||||
: CommContext(rank, size),
|
||||
nccl_version_(0),
|
||||
nccl_comm_(nullptr),
|
||||
nranks(size_),
|
||||
myrank(rank_),
|
||||
param(nccl_comm_init_option) {
|
||||
this->CreateNCCLComm(nccl_id, nccl_config_ptr);
|
||||
NCCL_CHECK(phi::dynload::ncclGetVersion(&nccl_version_));
|
||||
}
|
||||
|
||||
void NCCLCommContext::CreateNCCLComm(
|
||||
ncclUniqueId nccl_id,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr) {
|
||||
if (param > 0 && phi::dynload::ncclCommInitRank2.IsValid()) {
|
||||
LOG(WARNING) << "Creating modified qp with ncclCommInitRank2.";
|
||||
NCCL_CHECK(phi::dynload::ncclCommInitRank2(
|
||||
&nccl_comm_, nranks, nccl_id, myrank, param));
|
||||
} else {
|
||||
if (param > 0) {
|
||||
LOG(WARNING) << "ncclCommInitRank2 is not supported.";
|
||||
}
|
||||
if (nccl_config_ptr != nullptr &&
|
||||
phi::dynload::ncclCommInitRankConfigMemOpt.IsValid()) {
|
||||
NCCL_CHECK(phi::dynload::ncclCommInitRankConfigMemOpt(
|
||||
&nccl_comm_,
|
||||
nranks,
|
||||
nccl_id,
|
||||
myrank,
|
||||
nccl_config_ptr->GetOrigin(),
|
||||
nccl_config_ptr->GetMemOpt()));
|
||||
} else {
|
||||
if (nccl_config_ptr != nullptr) {
|
||||
LOG(WARNING) << "ncclCommInitRankConfigMemOpt is not supported.";
|
||||
}
|
||||
NCCL_CHECK(
|
||||
phi::dynload::ncclCommInitRank(&nccl_comm_, nranks, nccl_id, myrank));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NCCLCommContext::DestroyNCCLComm() {
|
||||
if (nccl_comm_ != nullptr) {
|
||||
NCCL_CHECK(phi::dynload::ncclCommDestroy(nccl_comm_));
|
||||
nccl_comm_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int NCCLCommContext::GetNcclVersion() { return nccl_version_; }
|
||||
|
||||
ncclComm_t NCCLCommContext::GetNcclComm() { return nccl_comm_; }
|
||||
|
||||
gpuStream_t NCCLCommContext::GetStream() { return dev_ctx_->stream(); }
|
||||
|
||||
phi::GPUContext* NCCLCommContext::GetDevContext() { return dev_ctx_.get(); }
|
||||
|
||||
void NCCLCommContext::SetDevContext(
|
||||
std::unique_ptr<phi::GPUContext>&& dev_ctx) {
|
||||
dev_ctx_ = std::move(dev_ctx);
|
||||
}
|
||||
|
||||
gpuEvent_t NCCLCommContext::GetComputeEvent() { return compute_event_.get(); }
|
||||
|
||||
void NCCLCommContext::SetComputeEvent(
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type>&&
|
||||
compute_event) {
|
||||
compute_event_ = std::move(compute_event);
|
||||
}
|
||||
|
||||
gpuEvent_t NCCLCommContext::GetCommEvent() { return comm_event_.get(); }
|
||||
|
||||
void NCCLCommContext::SetCommEvent(
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type>&& comm_event) {
|
||||
comm_event_ = std::move(comm_event);
|
||||
}
|
||||
|
||||
void NCCLCommContext::Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
gpuStream_t stream) {
|
||||
CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
NCCLDynamicCheck::CheckShape(*out_tensor, root, rank_, nccl_comm_);
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclBroadcast(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToNCCLDataType(in_tensor.type()),
|
||||
root,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
}
|
||||
|
||||
void NCCLCommContext::AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
gpuStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::GatherLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
phi::distributed::NCCLDynamicCheck::CheckShape(*out_tensor,
|
||||
/*root_rank*/ 0,
|
||||
rank_,
|
||||
nccl_comm_);
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclAllGather(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToNCCLDataType(in_tensor.type()),
|
||||
nccl_comm_,
|
||||
stream));
|
||||
}
|
||||
void NCCLCommContext::ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
in_tensor.dtype() != DataType::FLOAT8_E4M3FN &&
|
||||
in_tensor.dtype() != DataType::FLOAT8_E5M2,
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"float8 dtypes are not currently supported for NCCL reductions"));
|
||||
|
||||
phi::distributed::CommStaticCheck::ScatterLikeShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
phi::distributed::NCCLDynamicCheck::CheckShape(*out_tensor,
|
||||
/*root_rank*/ 0,
|
||||
rank_,
|
||||
nccl_comm_);
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclReduceScatter(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
out_tensor->numel(),
|
||||
ToNCCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
}
|
||||
|
||||
void NCCLCommContext::Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
gpuStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(in_tensor, rank_, size_);
|
||||
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
NCCLDynamicCheck::CheckShape(in_tensor, rank_, rank_, nccl_comm_);
|
||||
}
|
||||
|
||||
NCCL_CHECK(phi::dynload::ncclSend(in_tensor.data(),
|
||||
count,
|
||||
ToNCCLDataType(in_tensor.dtype()),
|
||||
peer,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
VLOG(3) << "rank " << GetRank() << " send "
|
||||
<< common::product(in_tensor.dims()) << " to " << peer;
|
||||
}
|
||||
|
||||
void NCCLCommContext::Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
gpuStream_t stream) {
|
||||
phi::distributed::CommStaticCheck::CheckShape(*out_tensor, rank_, size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
NCCLDynamicCheck::CheckShape(*out_tensor, peer, rank_, nccl_comm_);
|
||||
}
|
||||
|
||||
NCCL_CHECK(phi::dynload::ncclRecv(out_tensor->data(),
|
||||
count,
|
||||
ToNCCLDataType(out_tensor->dtype()),
|
||||
peer,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
VLOG(3) << "rank " << GetRank() << " recv "
|
||||
<< common::product(out_tensor->dims()) << " from " << peer;
|
||||
}
|
||||
|
||||
void NCCLCommContext::AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
in_tensor.dtype() != DataType::FLOAT8_E4M3FN &&
|
||||
in_tensor.dtype() != DataType::FLOAT8_E5M2,
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"float8 dtypes are not currently supported for NCCL reductions"));
|
||||
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ rank_,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
phi::distributed::NCCLDynamicCheck::CheckShape(*out_tensor,
|
||||
/*root_rank*/ 0,
|
||||
rank_,
|
||||
nccl_comm_);
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclAllReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToNCCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
}
|
||||
|
||||
void NCCLCommContext::Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
int root,
|
||||
gpuStream_t stream) {
|
||||
PADDLE_ENFORCE_EQ(
|
||||
in_tensor.dtype() != DataType::FLOAT8_E4M3FN &&
|
||||
in_tensor.dtype() != DataType::FLOAT8_E5M2,
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"float8 dtypes are not currently supported for NCCL reductions"));
|
||||
|
||||
phi::distributed::CommStaticCheck::SameShape(*out_tensor,
|
||||
in_tensor,
|
||||
/*dst_rank*/ root,
|
||||
/*cur_rank*/ rank_,
|
||||
size_);
|
||||
if (FLAGS_enable_nccl_dynamic_check) {
|
||||
phi::distributed::NCCLDynamicCheck::CheckShape(*out_tensor,
|
||||
/*root_rank*/ root,
|
||||
rank_,
|
||||
nccl_comm_);
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclReduce(in_tensor.data(),
|
||||
out_tensor->data(),
|
||||
in_tensor.numel(),
|
||||
ToNCCLDataType(in_tensor.type()),
|
||||
reduce_type,
|
||||
root,
|
||||
nccl_comm_,
|
||||
stream));
|
||||
}
|
||||
|
||||
void NCCLCommContext::GroupStart() {
|
||||
NCCL_CHECK(phi::dynload::ncclGroupStart());
|
||||
}
|
||||
void NCCLCommContext::GroupEnd() { NCCL_CHECK(phi::dynload::ncclGroupEnd()); }
|
||||
|
||||
#if NCCL_VERSION_CODE >= 21100
|
||||
void NCCLCommContext::RedOpCreatePreMulSum(ncclRedOp_t* op,
|
||||
void* scalar,
|
||||
ncclDataType_t dtype,
|
||||
ncclScalarResidence_t residence) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclRedOpCreatePreMulSum(
|
||||
op, scalar, dtype, residence, nccl_comm_));
|
||||
}
|
||||
|
||||
void NCCLCommContext::RedOpDestroy(ncclRedOp_t op) {
|
||||
PADDLE_ENFORCE_GPU_SUCCESS(phi::dynload::ncclRedOpDestroy(op, nccl_comm_));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,151 @@
|
||||
// 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
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
#include <cuda.h>
|
||||
#include <cuda_runtime.h>
|
||||
#endif
|
||||
|
||||
#ifdef PADDLE_WITH_HIP
|
||||
#include <hip/hip_runtime.h>
|
||||
#endif
|
||||
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_context.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
#include "paddle/phi/core/distributed/nccl_config.h"
|
||||
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
#else
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
class DenseTensor;
|
||||
namespace distributed {
|
||||
|
||||
class NCCLCommContext final : public CommContext {
|
||||
public:
|
||||
NCCLCommContext(
|
||||
int rank,
|
||||
int size,
|
||||
ncclUniqueId nccl_id,
|
||||
int nccl_comm_init_option = 0,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr = nullptr);
|
||||
~NCCLCommContext() override = default;
|
||||
|
||||
int GetNcclVersion();
|
||||
|
||||
ncclComm_t GetNcclComm();
|
||||
|
||||
void CreateNCCLComm(
|
||||
ncclUniqueId nccl_id,
|
||||
std::shared_ptr<phi::distributed::NCCLConfig> nccl_config_ptr = nullptr);
|
||||
|
||||
void DestroyNCCLComm();
|
||||
|
||||
gpuStream_t GetStream();
|
||||
|
||||
gpuEvent_t GetComputeEvent();
|
||||
|
||||
void SetComputeEvent(
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type>&&
|
||||
compute_event);
|
||||
|
||||
gpuEvent_t GetCommEvent();
|
||||
|
||||
void SetCommEvent(
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type>&& comm_event);
|
||||
|
||||
phi::GPUContext* GetDevContext();
|
||||
|
||||
void SetDevContext(std::unique_ptr<phi::GPUContext>&& dev_ctx);
|
||||
|
||||
void Broadcast(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
int root,
|
||||
gpuStream_t stream);
|
||||
|
||||
void Send(const DenseTensor& in_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
gpuStream_t stream);
|
||||
|
||||
void Recv(DenseTensor* out_tensor,
|
||||
const int64_t& count,
|
||||
const int& peer,
|
||||
gpuStream_t stream);
|
||||
|
||||
void ReduceScatter(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
gpuStream_t stream);
|
||||
|
||||
void AllGather(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
gpuStream_t stream);
|
||||
|
||||
void AllReduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
gpuStream_t stream);
|
||||
|
||||
void Reduce(DenseTensor* out_tensor,
|
||||
const DenseTensor& in_tensor,
|
||||
ncclRedOp_t reduce_type,
|
||||
int root,
|
||||
gpuStream_t stream);
|
||||
|
||||
void GroupStart();
|
||||
|
||||
void GroupEnd();
|
||||
|
||||
#if NCCL_VERSION_CODE >= 21100
|
||||
// Creates a new reduction operator which pre-multiplies input values by a
|
||||
// given scalar locally before reducing them with peer values via summation.
|
||||
void RedOpCreatePreMulSum(ncclRedOp_t* op,
|
||||
void* scalar,
|
||||
ncclDataType_t dtype,
|
||||
ncclScalarResidence_t residence);
|
||||
|
||||
// Destroys the reduction operator op. The operator must have been created by
|
||||
// ncclRedOpCreatePreMul with the matching communicator comm.
|
||||
void RedOpDestroy(ncclRedOp_t op);
|
||||
#endif
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(NCCLCommContext);
|
||||
|
||||
int nccl_version_;
|
||||
|
||||
ncclComm_t nccl_comm_;
|
||||
|
||||
std::unique_ptr<phi::GPUContext> dev_ctx_;
|
||||
|
||||
// used for comm wait compute, compute_stream-->event-->comm_stream
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type> compute_event_;
|
||||
|
||||
// used for compute wait comm, comm_stream-->event-->compute_stream
|
||||
std::shared_ptr<std::remove_pointer<phi::gpuEvent_t>::type> comm_event_;
|
||||
|
||||
int nranks;
|
||||
int myrank;
|
||||
int param;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,268 @@
|
||||
// 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/nccl_comm_task.h"
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
#include "paddle/phi/backends/gpu/gpu_info.h"
|
||||
#include "paddle/phi/core/distributed/comm_context_manager.h"
|
||||
#include "paddle/phi/core/distributed/nccl_tools.h"
|
||||
#include "paddle/phi/core/utils/data_type.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
NCCLCommTask::NCCLCommTask(const Place& place,
|
||||
const std::string& group_key,
|
||||
int rank,
|
||||
int size,
|
||||
int gid,
|
||||
uint64_t seq,
|
||||
int64_t numel,
|
||||
bool sync_op,
|
||||
bool use_calc_stream,
|
||||
ncclComm_t nccl_comm,
|
||||
gpuStream_t stream,
|
||||
CommType comm_type,
|
||||
int64_t timeout)
|
||||
: CommTask("NCCL",
|
||||
place,
|
||||
group_key,
|
||||
rank,
|
||||
size,
|
||||
gid,
|
||||
seq,
|
||||
numel,
|
||||
nccl_comm,
|
||||
stream,
|
||||
comm_type),
|
||||
timeout_(std::chrono::milliseconds(timeout)),
|
||||
sync_op_(sync_op),
|
||||
use_calc_stream_(use_calc_stream),
|
||||
nccl_start_event_(nullptr),
|
||||
nccl_end_event_(nullptr) {
|
||||
start_trace_updated_ = false;
|
||||
start_event_created_ = false;
|
||||
end_event_created_ = false;
|
||||
start_time_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void NCCLCommTask::StartRecord() {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
if (!start_event_created_) {
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
CUDA_CHECK(cudaEventCreateWithFlags(&nccl_start_event_, cuda_event_flags_));
|
||||
#else // PADDLE_WITH_HIP
|
||||
HIP_CHECK(hipEventCreateWithFlags(&nccl_start_event_, hip_event_flags_));
|
||||
#endif
|
||||
start_event_created_ = true;
|
||||
}
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
CUDA_CHECK(cudaEventRecord(nccl_start_event_, nccl_stream_));
|
||||
#else // PADDLE_WITH_HIP
|
||||
HIP_CHECK(hipEventRecord(nccl_start_event_, nccl_stream_));
|
||||
#endif
|
||||
}
|
||||
void NCCLCommTask::EndRecord() {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
if (!end_event_created_) {
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
CUDA_CHECK(cudaEventCreateWithFlags(&nccl_end_event_, cuda_event_flags_));
|
||||
#else // PADDLE_WITH_HIP
|
||||
HIP_CHECK(hipEventCreateWithFlags(&nccl_end_event_, hip_event_flags_));
|
||||
#endif
|
||||
end_event_created_ = true;
|
||||
}
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
CUDA_CHECK(cudaEventRecord(nccl_end_event_, nccl_stream_));
|
||||
#else // PADDLE_WITH_HIP
|
||||
HIP_CHECK(hipEventRecord(nccl_end_event_, nccl_stream_));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
void NCCLCommTask::ClearRecord() {
|
||||
if (start_event_created_) {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
CUDA_CHECK(cudaEventDestroy(nccl_start_event_));
|
||||
start_event_created_ = false;
|
||||
}
|
||||
if (end_event_created_) {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
CUDA_CHECK(cudaEventDestroy(nccl_end_event_));
|
||||
end_event_created_ = false;
|
||||
}
|
||||
}
|
||||
#else // PADDLE_WITH_HIP
|
||||
void NCCLCommTask::ClearRecord() {
|
||||
if (start_event_created_) {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
HIP_CHECK(hipEventDestroy(nccl_start_event_));
|
||||
start_event_created_ = false;
|
||||
}
|
||||
if (end_event_created_) {
|
||||
backends::gpu::GPUDeviceGuard guard(place_.device);
|
||||
HIP_CHECK(hipEventDestroy(nccl_end_event_));
|
||||
end_event_created_ = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool NCCLCommTask::CudaEventQuery(gpuEvent_t event) {
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
cudaError_t ret = cudaEventQuery(event);
|
||||
if (ret == cudaSuccess) {
|
||||
return true;
|
||||
} else if (ret != cudaErrorNotReady) {
|
||||
CUDA_CHECK(ret);
|
||||
} else {
|
||||
// ignore and clear the error if not ready
|
||||
CUDA_CHECK(cudaGetLastError());
|
||||
}
|
||||
#else // PADDLE_WITH_HIP
|
||||
hipError_t ret = hipEventQuery(event);
|
||||
if (ret == hipSuccess) {
|
||||
return true;
|
||||
} else if (ret != hipErrorNotReady) {
|
||||
HIP_CHECK(ret);
|
||||
} else {
|
||||
// ignore and clear the error if not ready
|
||||
HIP_CHECK(hipGetLastError());
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetNCCLErrorDetail(ncclResult_t result) {
|
||||
std::string detail;
|
||||
std::string last_error;
|
||||
#ifdef ENABLE_NCCL_GET_LAST_ERROR
|
||||
last_error =
|
||||
", Last error: " + std::string(phi::dynload::ncclGetLastError(NULL));
|
||||
#endif
|
||||
switch (result) {
|
||||
case ncclUnhandledCudaError:
|
||||
detail = "ncclUnhandledCudaError: Call to CUDA function failed.";
|
||||
break;
|
||||
case ncclSystemError:
|
||||
detail =
|
||||
"ncclSystemError: System call (e.g. socket, malloc) or external "
|
||||
"library call failed or device error. ";
|
||||
#ifndef NCCL_REMOTE_ERROR
|
||||
// Before ncclRemoteError was created, unexpected remote disconnect was
|
||||
// categorized as ncclSystemError
|
||||
detail += "It can be also caused by unexpected exit of a remote peer.";
|
||||
#endif
|
||||
break;
|
||||
case ncclInternalError:
|
||||
detail = "ncclInternalError: Internal check failed.";
|
||||
break;
|
||||
case ncclInvalidArgument:
|
||||
detail = "ncclInvalidArgument: Invalid value for an argument.";
|
||||
break;
|
||||
case ncclInvalidUsage:
|
||||
detail =
|
||||
"ncclInvalidUsage: This usually reflects invalid usage of NCCL "
|
||||
"library.";
|
||||
break;
|
||||
#ifdef NCCL_REMOTE_ERROR
|
||||
case ncclRemoteError:
|
||||
detail =
|
||||
"ncclRemoteError: A call failed possibly due to a network error or a "
|
||||
"remote process exiting prematurely.";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
detail = "Unknown NCCL error!";
|
||||
}
|
||||
return detail + last_error;
|
||||
}
|
||||
|
||||
std::string NCCLCommTask::GetCommErrors() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (!comm_error_.empty()) {
|
||||
return comm_error_;
|
||||
}
|
||||
|
||||
ncclResult_t nccl_async_error;
|
||||
NCCL_CHECK(
|
||||
phi::dynload::ncclCommGetAsyncError(nccl_comm_, &nccl_async_error));
|
||||
if (nccl_async_error != ncclSuccess) {
|
||||
comm_error_ =
|
||||
"\n\t Find nccl comm error: " + GetNCCLErrorDetail(nccl_async_error);
|
||||
}
|
||||
return comm_error_;
|
||||
}
|
||||
|
||||
bool NCCLCommTask::IsStarted() {
|
||||
if (started_) {
|
||||
return true;
|
||||
}
|
||||
if (start_event_created_ && CudaEventQuery(nccl_start_event_)) {
|
||||
started_ = true;
|
||||
updated_ = true;
|
||||
}
|
||||
return started_;
|
||||
}
|
||||
|
||||
bool NCCLCommTask::IsCompleted() {
|
||||
if (completed_) {
|
||||
return true;
|
||||
}
|
||||
if (end_event_created_ && CudaEventQuery(nccl_end_event_)) {
|
||||
completed_ = true;
|
||||
updated_ = true;
|
||||
}
|
||||
return completed_;
|
||||
}
|
||||
|
||||
void NCCLCommTask::SetUpdated(bool updated) { updated_ = updated; }
|
||||
|
||||
bool NCCLCommTask::IsUpdated() { return updated_; }
|
||||
|
||||
bool NCCLCommTask::IsTimeout() {
|
||||
auto current_timepoint = std::chrono::steady_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
current_timepoint - start_time_) >= timeout_;
|
||||
}
|
||||
|
||||
void NCCLCommTask::AbortComm() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
if (aborted_) {
|
||||
return;
|
||||
}
|
||||
NCCL_CHECK(phi::dynload::ncclCommAbort(nccl_comm_));
|
||||
|
||||
aborted_ = true;
|
||||
nccl_comm_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string NCCLCommTask::GetTraceMsg() {
|
||||
auto global_ranks =
|
||||
phi::distributed::CommContextManager::GetInstance().GetGroupRanks(
|
||||
group_key_);
|
||||
return "group_key:" + group_key_ +
|
||||
",group_ranks:" + VectorToString(global_ranks) +
|
||||
",global_rank:" + std::to_string(global_rank_) +
|
||||
",local_rank:" + std::to_string(rank_) +
|
||||
",comm_count:" + std::to_string(seq_) +
|
||||
",op:" + CommTypeToString(comm_type_) +
|
||||
",started:" + std::to_string(started_) +
|
||||
",completed:" + std::to_string(completed_) +
|
||||
",numel:" + std::to_string(numel_) +
|
||||
",nranks:" + std::to_string(size_);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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/common/macros.h"
|
||||
#include "paddle/phi/backends/gpu/gpu_decls.h"
|
||||
#include "paddle/phi/core/distributed/comm_context.h"
|
||||
#include "paddle/phi/core/distributed/comm_task.h"
|
||||
#include "paddle/phi/core/distributed/utils.h"
|
||||
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
#else
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
class DenseTensor;
|
||||
namespace distributed {
|
||||
|
||||
static int64_t DefaultTimeout = 30 * 60 * 1000;
|
||||
|
||||
class NCCLCommTask : public CommTask {
|
||||
public:
|
||||
NCCLCommTask(const Place& place = Place(),
|
||||
const std::string& group_key = "",
|
||||
int rank = -1,
|
||||
int size = 0,
|
||||
int gid = 0,
|
||||
uint64_t seq = 0,
|
||||
int64_t numel = 0,
|
||||
bool sync_op = true,
|
||||
bool use_calc_stream = false,
|
||||
ncclComm_t = nullptr,
|
||||
gpuStream_t = nullptr,
|
||||
CommType comm_type = CommType::UNKNOWN,
|
||||
int64_t timeout = DefaultTimeout);
|
||||
~NCCLCommTask() override = default;
|
||||
|
||||
// check whether the nccl kernel started
|
||||
bool IsStarted() override;
|
||||
bool IsTimeout() override;
|
||||
bool IsCompleted() override;
|
||||
void SetUpdated(bool updated) override;
|
||||
bool IsUpdated() override;
|
||||
|
||||
std::string GetTraceMsg() override;
|
||||
std::string GetCommErrors() override;
|
||||
void AbortComm() override;
|
||||
|
||||
void StartRecord() override;
|
||||
void EndRecord() override;
|
||||
void ClearRecord() override;
|
||||
|
||||
bool CudaEventQuery(gpuEvent_t event);
|
||||
|
||||
protected:
|
||||
std::mutex mutex_;
|
||||
std::chrono::milliseconds timeout_;
|
||||
|
||||
#ifdef PADDLE_WITH_CUDA
|
||||
unsigned int cuda_event_flags_ = cudaEventDisableTiming;
|
||||
#else // PADDLE_WITH_HIP
|
||||
unsigned int hip_event_flags_ = hipEventDisableTiming;
|
||||
#endif
|
||||
|
||||
bool sync_op_;
|
||||
bool use_calc_stream_;
|
||||
|
||||
bool start_event_created_;
|
||||
bool end_event_created_;
|
||||
gpuEvent_t nccl_start_event_;
|
||||
gpuEvent_t nccl_end_event_;
|
||||
|
||||
std::string comm_error_;
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(NCCLCommTask);
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2025 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/nccl_config.h"
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
std::shared_ptr<NCCLConfig> NCCLConfig::CreateNCCLConfig(
|
||||
const std::string& commName,
|
||||
const int ll_buffsize,
|
||||
const int ll128_buffsize,
|
||||
const int simple_buffsize,
|
||||
const int buffsize_align,
|
||||
const int nchannels,
|
||||
const std::string& algoStr,
|
||||
const std::string& protoStr) {
|
||||
return std::make_shared<NCCLConfig>(commName,
|
||||
ll_buffsize,
|
||||
ll128_buffsize,
|
||||
simple_buffsize,
|
||||
buffsize_align,
|
||||
nchannels,
|
||||
algoStr,
|
||||
protoStr);
|
||||
}
|
||||
|
||||
NCCLConfig::NCCLConfig(const std::string& commName,
|
||||
const int ll_buffsize,
|
||||
const int ll128_buffsize,
|
||||
const int simple_buffsize,
|
||||
const int buffsize_align,
|
||||
const int nchannels,
|
||||
const std::string& algoStr,
|
||||
const std::string& protoStr)
|
||||
: commName_(commName),
|
||||
ll_buffsize_(ll_buffsize),
|
||||
ll128_buffsize_(ll128_buffsize),
|
||||
simple_buffsize_(simple_buffsize),
|
||||
buffsize_align_(buffsize_align),
|
||||
nchannels_(nchannels),
|
||||
algoStr_(algoStr),
|
||||
protoStr_(protoStr),
|
||||
nccl_memopt_config_ptr(nullptr) {
|
||||
if (phi::dynload::ncclCommGenMemOptConfig.IsValid()) {
|
||||
nccl_memopt_config_ptr = phi::dynload::ncclCommGenMemOptConfig(
|
||||
commName_.empty() ? nullptr : commName_.c_str(),
|
||||
ll_buffsize_ >= 0 ? ll_buffsize_ : -1,
|
||||
ll128_buffsize_ >= 0 ? ll128_buffsize_ : -1,
|
||||
simple_buffsize_ >= 0 ? simple_buffsize_ : -1,
|
||||
buffsize_align_ >= 0 ? buffsize_align_ : -1,
|
||||
nchannels_ >= 0 ? nchannels_ : -1,
|
||||
algoStr_.empty() ? nullptr : algoStr_.c_str(),
|
||||
protoStr_.empty() ? nullptr : protoStr_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ncclConfig_t* NCCLConfig::GetOrigin() { return nullptr; }
|
||||
|
||||
ncclMemOptConfig_t* NCCLConfig::GetMemOpt() { return nccl_memopt_config_ptr; }
|
||||
|
||||
NCCLConfig::~NCCLConfig() {
|
||||
if (phi::dynload::ncclCommFreeMemOptConfig.IsValid() &&
|
||||
nccl_memopt_config_ptr != nullptr) {
|
||||
phi::dynload::ncclCommFreeMemOptConfig(nccl_memopt_config_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2025 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>
|
||||
#if defined(PADDLE_WITH_RCCL)
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
#else
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class NCCLConfig {
|
||||
public:
|
||||
static std::shared_ptr<NCCLConfig> CreateNCCLConfig(
|
||||
const std::string& commName = "",
|
||||
const int ll_buffsize = -1,
|
||||
const int ll128_buffsize = -1,
|
||||
const int simple_buffsize = -1,
|
||||
const int buffsize_align = -1,
|
||||
const int nchannels = -1,
|
||||
const std::string& algoStr = "",
|
||||
const std::string& protoStr = "");
|
||||
ncclConfig_t* GetOrigin();
|
||||
ncclMemOptConfig_t* GetMemOpt();
|
||||
|
||||
NCCLConfig(const std::string& commName,
|
||||
const int ll_buffsize,
|
||||
const int ll128_buffsize,
|
||||
const int simple_buffsize,
|
||||
const int buffsize_align,
|
||||
const int nchannels,
|
||||
const std::string& algoStr,
|
||||
const std::string& protoStr);
|
||||
~NCCLConfig();
|
||||
|
||||
private:
|
||||
const std::string commName_;
|
||||
const int ll_buffsize_;
|
||||
const int ll128_buffsize_;
|
||||
const int simple_buffsize_;
|
||||
const int buffsize_align_;
|
||||
const int nchannels_;
|
||||
const std::string algoStr_;
|
||||
const std::string protoStr_;
|
||||
|
||||
ncclMemOptConfig_t* nccl_memopt_config_ptr{nullptr};
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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/nccl_tools.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "paddle/common/errors.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
#if NCCL_VERSION_CODE >= 21300
|
||||
#define ENABLE_NCCL_GET_LAST_ERROR
|
||||
#define NCCL_REMOTE_ERROR
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
ncclRedOp_t ToNCCLRedType(ReduceOp reduction) {
|
||||
static const std::unordered_map<ReduceOp, ncclRedOp_t> red_type = {
|
||||
{ReduceOp::MIN, ncclMin},
|
||||
{ReduceOp::MAX, ncclMax},
|
||||
{ReduceOp::SUM, ncclSum},
|
||||
{ReduceOp::PRODUCT, ncclProd},
|
||||
#if NCCL_VERSION_CODE >= 21000
|
||||
{ReduceOp::AVG, ncclAvg},
|
||||
#endif
|
||||
};
|
||||
auto it = red_type.find(reduction);
|
||||
PADDLE_ENFORCE_EQ(it != red_type.end(),
|
||||
true,
|
||||
common::errors::InvalidArgument(
|
||||
"Invalid nccl reduction. Must be ncclMin | ncclMax | "
|
||||
"ncclProd | ncclSum | ncclAvg."));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string SerializeNCCLUniqueId(const ncclUniqueId& ncclID) {
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&ncclID);
|
||||
std::ostringstream oss;
|
||||
for (auto i = 0; i < NCCL_UNIQUE_ID_BYTES; ++i) {
|
||||
oss << std::hex << static_cast<int>(bytes[i]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string NCCLDTypeToString(ncclDataType_t dtype) {
|
||||
#define PD_NCCL_DTYPE_TO_STR(__nccl_dtype, __str_dtype) \
|
||||
if (dtype == __nccl_dtype) return __str_dtype;
|
||||
PD_NCCL_DTYPE_TO_STR(ncclFloat, "float32");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclFloat32, "float32");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclHalf, "float16");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclFloat16, "float16");
|
||||
#if (NCCL_VERSION_CODE >= 21000 && CUDA_VERSION >= 11000) || \
|
||||
defined(PADDLE_WITH_HIP)
|
||||
PD_NCCL_DTYPE_TO_STR(ncclBfloat16, "bfloat16");
|
||||
#endif
|
||||
PD_NCCL_DTYPE_TO_STR(ncclDouble, "float64");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclFloat64, "float64");
|
||||
|
||||
PD_NCCL_DTYPE_TO_STR(ncclInt8, "int8");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclChar, "int8");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclUint8, "uint8");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclInt32, "int32");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclInt, "int32");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclUint32, "uint32");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclInt64, "int64");
|
||||
PD_NCCL_DTYPE_TO_STR(ncclUint64, "uint64");
|
||||
|
||||
#undef PD_NCCL_DTYPE_TO_STR
|
||||
PADDLE_THROW(common::errors::InvalidArgument(
|
||||
"This datatype %d in nccl is not supported.", static_cast<int>(dtype)));
|
||||
}
|
||||
|
||||
std::string NCCLRedTypeToString(ncclRedOp_t op) {
|
||||
if (op == ncclSum) return "SUM";
|
||||
if (op == ncclProd) return "PROD";
|
||||
if (op == ncclMin) return "MIN";
|
||||
if (op == ncclMax) return "MAX";
|
||||
#if NCCL_VERSION_CODE >= 21000
|
||||
if (op == ncclAvg) return "AVG";
|
||||
#endif
|
||||
return "UDF_" + std::to_string(op);
|
||||
}
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 <string>
|
||||
|
||||
#include "paddle/phi/core/distributed/types.h"
|
||||
|
||||
#ifdef PADDLE_WITH_RCCL
|
||||
#include <hip/hip_runtime.h>
|
||||
#include "paddle/phi/backends/dynload/rccl.h"
|
||||
#else
|
||||
#include <cuda_runtime.h>
|
||||
#include "paddle/phi/backends/dynload/nccl.h"
|
||||
#endif
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
#define NCCL_CHECK(cmd) \
|
||||
do { \
|
||||
ncclResult_t r = cmd; \
|
||||
if (r != ncclSuccess) { \
|
||||
PADDLE_THROW( \
|
||||
common::errors::External("Failed, NCCL error %s:%d '%s'\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
phi::dynload::ncclGetErrorString(r))); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef PADDLE_WITH_NCCL
|
||||
#define CUDA_CHECK(expr) \
|
||||
do { \
|
||||
cudaError_t r = expr; \
|
||||
if (r != cudaSuccess) { \
|
||||
PADDLE_THROW(common::errors::External("Failed, cuda error %s:%d '%s'\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
cudaGetErrorString(r))); \
|
||||
} \
|
||||
} while (0)
|
||||
#else // PADDLE_WITH_RCCL
|
||||
#define HIP_CHECK(expr) \
|
||||
do { \
|
||||
hipError_t r = expr; \
|
||||
if (r != hipSuccess) { \
|
||||
PADDLE_THROW(common::errors::External("Failed, hip error %s:%d '%s'\n", \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
hipGetErrorString(r))); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
ncclRedOp_t ToNCCLRedType(ReduceOp reduction);
|
||||
|
||||
std::string SerializeNCCLUniqueId(const ncclUniqueId& ncclID);
|
||||
|
||||
std::string NCCLDTypeToString(ncclDataType_t dtype);
|
||||
|
||||
std::string NCCLRedTypeToString(ncclRedOp_t op);
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,8 @@
|
||||
set(STORE_COMMON_SRCS tcp_store.cc tcp_store_libuv.cc tcp_utils.cc socket.cpp
|
||||
store.cc store_utils.cc)
|
||||
|
||||
if(WITH_GLOO)
|
||||
list(APPEND STORE_COMMON_SRCS gloo_store.cc)
|
||||
endif()
|
||||
|
||||
collect_srcs(core_srcs SRCS ${STORE_COMMON_SRCS})
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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/store/gloo_store.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
GlooStore::GlooStore(const std::shared_ptr<phi::distributed::Store>& store)
|
||||
: store_(store) {}
|
||||
|
||||
std::vector<char> GlooStore::get(const std::string& key) {
|
||||
auto value = store_->get(key);
|
||||
return std::vector<char>(value.begin(), value.end());
|
||||
}
|
||||
|
||||
void GlooStore::wait(const std::vector<std::string>& keys) {
|
||||
for (auto& key : keys) {
|
||||
store_->wait(key);
|
||||
}
|
||||
}
|
||||
|
||||
void GlooStore::set(const std::string& key, const std::vector<char>& value) {
|
||||
std::vector<uint8_t> tmp(value.begin(), value.end());
|
||||
store_->set(key, tmp);
|
||||
}
|
||||
|
||||
void GlooStore::wait(const std::vector<std::string>& keys,
|
||||
const std::chrono::milliseconds& timeout) {
|
||||
for (auto& key : keys) {
|
||||
store_->wait(key);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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 <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "gloo/rendezvous/store.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/store/store.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class GlooStore : public gloo::rendezvous::Store {
|
||||
public:
|
||||
explicit GlooStore(const std::shared_ptr<phi::distributed::Store>& store);
|
||||
|
||||
~GlooStore() = default;
|
||||
|
||||
std::vector<char> get(const std::string& key) override;
|
||||
|
||||
void wait(const std::vector<std::string>& keys) override;
|
||||
|
||||
void set(const std::string& key, const std::vector<char>& value) override;
|
||||
|
||||
void wait(const std::vector<std::string>& keys,
|
||||
const std::chrono::milliseconds& timeout) override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<phi::distributed::Store> store_;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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/store/socket.h"
|
||||
#include <array>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
#ifdef _WIN32
|
||||
static int _get_sockname_of_win(int sock, char* out, int out_len) {
|
||||
snprintf(out, out_len, "not support win now");
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int _get_sockname(int sock, char *out, int out_len) {
|
||||
struct sockaddr_in addr = {};
|
||||
socklen_t s_len = sizeof(addr);
|
||||
|
||||
if (::getpeername(sock, reinterpret_cast<sockaddr *>(&addr), &s_len)) {
|
||||
::snprintf(
|
||||
out, out_len, "can't getsocketname of %d, errno:%d", sock, errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::array<char, 128> ip = {};
|
||||
int port = 0;
|
||||
|
||||
// deal with both IPv4 and IPv6:
|
||||
if (addr.sin_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
port = ntohs(s->sin_port);
|
||||
::inet_ntop(AF_INET, &s->sin_addr, ip.data(), sizeof(ip));
|
||||
} else { // AF_INET6
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
||||
port = ntohs(s->sin6_port);
|
||||
::inet_ntop(AF_INET6, &s->sin6_addr, ip.data(), sizeof(ip));
|
||||
}
|
||||
|
||||
::snprintf(out, out_len, "%s:%d", ip.data(), port);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int GetSockName(int sock, char* out, int out_len) {
|
||||
#ifdef _WIN32
|
||||
return _get_sockname_of_win(sock, out, out_len);
|
||||
#else
|
||||
return _get_sockname(sock, out, out_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string GetSockName(int fd) {
|
||||
std::array<char, 256> out = {};
|
||||
GetSockName(fd, out.data(), sizeof(out));
|
||||
return std::string(out.data());
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2021 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>
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
int GetSockName(int fd, char* out, int out_len);
|
||||
|
||||
std::string GetSockName(int fd);
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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/store/store.h"
|
||||
#include "paddle/phi/core/enforce.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
|
||||
int64_t Store::add(const std::string& key, int64_t value) {
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Implement the add method in the subclass."));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Store::get(const std::string& key) {
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Implement the get method in the subclass."));
|
||||
}
|
||||
|
||||
bool Store::check(const std::string& key) {
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Implement the get method in the subclass."));
|
||||
}
|
||||
|
||||
void Store::wait(const std::string& key) {
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Implement the wait method in the subclass."));
|
||||
}
|
||||
|
||||
void Store::set(const std::string& key, const std::vector<uint8_t>& value) {
|
||||
PADDLE_THROW(
|
||||
errors::InvalidArgument("Implement the set method in the subclass."));
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 <chrono>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "paddle/common/macros.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
class PADDLE_API Store {
|
||||
public:
|
||||
Store() : _timeout(900) {}
|
||||
explicit Store(const int timeout) : _timeout(timeout) {}
|
||||
virtual ~Store() = default;
|
||||
|
||||
virtual int64_t add(const std::string& key, int64_t value);
|
||||
virtual std::vector<uint8_t> get(const std::string& key);
|
||||
virtual bool check(const std::string& key);
|
||||
virtual void wait(const std::string& key);
|
||||
virtual void set(const std::string& key, const std::vector<uint8_t>& value);
|
||||
|
||||
virtual int timeout() { return _timeout; }
|
||||
|
||||
protected:
|
||||
int _timeout;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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/store/store_utils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// the <winsock2.h> needs to be included before <winsock.h>, otherwise
|
||||
// there will be symbol redefinition error on windows
|
||||
#include "paddle/phi/core/distributed/store/tcp_store.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
|
||||
|
||||
namespace phi::distributed {
|
||||
using auto_parallel::str_split;
|
||||
|
||||
namespace {
|
||||
std::string GetMasterEndpoint() {
|
||||
const char* master_endpoint = std::getenv("PADDLE_MASTER");
|
||||
if (!master_endpoint) {
|
||||
const char* trainer_endpoints = std::getenv("PADDLE_TRAINER_ENDPOINTS");
|
||||
PADDLE_ENFORCE_NOT_NULL(trainer_endpoints,
|
||||
common::errors::NotFound(
|
||||
"The environment variable "
|
||||
"'PADDLE_TRAINER_ENDPOINTS' cannot be found."));
|
||||
return str_split(trainer_endpoints, ",")[0];
|
||||
}
|
||||
|
||||
PADDLE_ENFORCE_NOT_NULL(
|
||||
master_endpoint,
|
||||
common::errors::NotFound(
|
||||
"The environment variable 'PADDLE_MASTER' cannot be found."));
|
||||
return master_endpoint;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int64_t GetCurGlobalRank() {
|
||||
const char* cur_rank = std::getenv("PADDLE_TRAINER_ID");
|
||||
if (cur_rank == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return std::atoi(cur_rank);
|
||||
}
|
||||
|
||||
int64_t GetGlobalWorldSize() {
|
||||
const char* world_size = std::getenv("PADDLE_TRAINERS_NUM");
|
||||
if (world_size == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
return std::atoi(world_size);
|
||||
}
|
||||
|
||||
std::string GetMasterAddr() {
|
||||
std::string master_endpoint = GetMasterEndpoint();
|
||||
return str_split(master_endpoint, ":")[0];
|
||||
}
|
||||
|
||||
uint16_t GetMasterPort() {
|
||||
std::string master_endpoint = GetMasterEndpoint();
|
||||
return std::stoi(str_split(master_endpoint, ":")[1]);
|
||||
}
|
||||
|
||||
std::shared_ptr<Store> CreateOrGetGlobalTCPStore() {
|
||||
std::string host = GetMasterAddr();
|
||||
uint16_t port = GetMasterPort();
|
||||
int64_t cur_rank = GetCurGlobalRank();
|
||||
int64_t world_size = GetGlobalWorldSize();
|
||||
bool is_master = (cur_rank == 0);
|
||||
|
||||
static std::shared_ptr<TCPStore> store =
|
||||
std::make_shared<TCPStore>(host, port, is_master, world_size);
|
||||
return store;
|
||||
}
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
#include "paddle/common/macros.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
class Store;
|
||||
|
||||
PADDLE_API int64_t GetCurGlobalRank();
|
||||
|
||||
PADDLE_API std::string GetMasterAddr();
|
||||
|
||||
PADDLE_API int64_t GetGlobalWorldSize();
|
||||
|
||||
PADDLE_API uint16_t GetMasterPort();
|
||||
|
||||
PADDLE_API std::shared_ptr<Store> CreateOrGetGlobalTCPStore();
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,494 @@
|
||||
// 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/store/tcp_store.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
#include "paddle/common/flags.h"
|
||||
#include "paddle/phi/core/distributed/store/tcp_utils.h"
|
||||
|
||||
COMMON_DECLARE_bool(tcp_store_using_libuv);
|
||||
namespace phi::distributed::detail {
|
||||
|
||||
// DaemonThread thread parent class methods
|
||||
DaemonThread::~DaemonThread() = default;
|
||||
|
||||
void DaemonThread::start() {
|
||||
daemonThread_ = std::thread{&DaemonThread::run, this};
|
||||
is_running_.store(true);
|
||||
}
|
||||
|
||||
void DaemonThread::cleanup() {
|
||||
stop();
|
||||
daemonThread_.join();
|
||||
}
|
||||
|
||||
bool DaemonThread::is_running() { return is_running_.load(); }
|
||||
|
||||
constexpr int INFTIME = 10000; // 10 seconds
|
||||
|
||||
std::unique_ptr<MasterDaemon> MasterDaemon::createDaemon(SocketType socket,
|
||||
int nranks,
|
||||
int timeout) {
|
||||
VLOG(8) << ("begin to run start");
|
||||
return std::make_unique<MasterDaemon>(socket, nranks, timeout);
|
||||
}
|
||||
|
||||
MasterDaemon::MasterDaemon(SocketType socket, int nranks, int timeout)
|
||||
: _listen_socket(socket), _nranks(nranks), _timeout(timeout) {
|
||||
InitControlFd();
|
||||
}
|
||||
|
||||
MasterDaemon::~MasterDaemon() { // NOLINT
|
||||
VLOG(8) << ("begin to destruct MasterDaemon");
|
||||
StopByControlFd();
|
||||
cleanup();
|
||||
tcputils::close_socket(_listen_socket);
|
||||
for (SocketType socket : _sockets) {
|
||||
tcputils::close_socket(socket);
|
||||
}
|
||||
CloseControlFd();
|
||||
}
|
||||
|
||||
void MasterDaemon::_do_add(SocketType socket) {
|
||||
int64_t new_value{};
|
||||
std::string key = tcputils::receive_string(socket);
|
||||
new_value = tcputils::receive_value<int64_t>(socket);
|
||||
std::vector<uint8_t> old_value;
|
||||
auto it = _store.find(key);
|
||||
if (it != _store.end()) {
|
||||
old_value = it->second;
|
||||
char* buffer = reinterpret_cast<char*>(it->second.data());
|
||||
size_t len = old_value.size();
|
||||
new_value += std::stoll(std::string(buffer, len));
|
||||
}
|
||||
|
||||
std::string new_value_str = std::to_string(new_value);
|
||||
_store[key] =
|
||||
std::vector<uint8_t>(new_value_str.begin(), new_value_str.end());
|
||||
VLOG(8) << "TCPStore: new value (" << new_value << ") for key (" << key
|
||||
<< ") " << GetSockName(socket);
|
||||
tcputils::send_value<int64_t>(socket, new_value);
|
||||
_notify_waiting_sockets(key);
|
||||
}
|
||||
|
||||
void MasterDaemon::_do_set(SocketType socket) {
|
||||
std::string key = tcputils::receive_string(socket);
|
||||
VLOG(8) << "MasterDaemon::_do_set key(" << key << ") " << GetSockName(socket);
|
||||
|
||||
auto value = tcputils::receive_vector<uint8_t>(socket);
|
||||
_store[key] = value;
|
||||
_notify_waiting_sockets(key);
|
||||
}
|
||||
|
||||
void MasterDaemon::_notify_waiting_sockets(const std::string& key) {
|
||||
if (_waiting_sockets.find(key) != _waiting_sockets.end()) {
|
||||
for (auto waiting_socket : _waiting_sockets.at(key)) {
|
||||
auto reply = ReplyType::STOP_WAIT;
|
||||
VLOG(7) << "TCPStore: notify the socket: " << GetSockName(waiting_socket)
|
||||
<< " that key: " << key << " is ready.";
|
||||
tcputils::send_value<ReplyType>(waiting_socket, reply);
|
||||
}
|
||||
_waiting_sockets.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void MasterDaemon::_do_get(SocketType socket) {
|
||||
std::string key = tcputils::receive_string(socket);
|
||||
VLOG(8) << "MasterDaemon::_do_get key(" << key << ") " << GetSockName(socket);
|
||||
|
||||
auto iter = _store.find(key);
|
||||
PADDLE_ENFORCE_NE(
|
||||
iter,
|
||||
_store.end(),
|
||||
common::errors::InvalidArgument("Key %s not found in TCPStore.", key));
|
||||
std::vector<uint8_t> value = iter->second;
|
||||
tcputils::send_vector<uint8_t>(socket, value);
|
||||
}
|
||||
|
||||
void MasterDaemon::_do_check(SocketType socket) {
|
||||
std::string key = tcputils::receive_string(socket);
|
||||
VLOG(4) << "MasterDaemon::_do_check key(" << key << ") "
|
||||
<< GetSockName(socket);
|
||||
|
||||
auto iter = _store.find(key);
|
||||
if (iter != _store.end()) {
|
||||
tcputils::send_value<ReplyType>(socket, ReplyType::READY);
|
||||
} else {
|
||||
tcputils::send_value<ReplyType>(socket, ReplyType::NOT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void MasterDaemon::InitControlFd() {
|
||||
PADDLE_ENFORCE_NE(
|
||||
pipe(_control_fd.data()),
|
||||
-1,
|
||||
common::errors::Fatal("failed to cread control pipe errno:%d", errno));
|
||||
}
|
||||
void MasterDaemon::CloseControlFd() {
|
||||
for (int fd : _control_fd) {
|
||||
if (fd != -1) {
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
void MasterDaemon::StopByControlFd() {
|
||||
VLOG(8) << ("begin to run StopByControlFd");
|
||||
if (_control_fd[1] != -1) {
|
||||
PADDLE_ENFORCE_NE(
|
||||
::write(_control_fd[1], "\0", 1),
|
||||
-1,
|
||||
common::errors::Fatal("failed to write control pipe errno:%d", errno));
|
||||
// close the write end of the pipe
|
||||
::close(_control_fd[1]);
|
||||
_control_fd[1] = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void MasterDaemon::InitControlFd() {
|
||||
ghStopEvent_ = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
PADDLE_ENFORCE_NE(ghStopEvent_,
|
||||
nullptr,
|
||||
common::errors::Fatal("failed to cread control pipe"));
|
||||
}
|
||||
void MasterDaemon::CloseControlFd() { CloseHandle(ghStopEvent_); }
|
||||
void MasterDaemon::StopByControlFd() { SetEvent(ghStopEvent_); }
|
||||
#endif
|
||||
|
||||
void MasterDaemon::_do_wait(SocketType socket) {
|
||||
std::string key = tcputils::receive_string(socket);
|
||||
VLOG(8) << "MasterDaemon::_do_wait key(" << key << ") "
|
||||
<< GetSockName(socket);
|
||||
|
||||
auto iter = _store.find(key);
|
||||
if (iter == _store.end()) {
|
||||
// The key can not be found in store currently. Record and check later.
|
||||
_waiting_sockets[key].emplace_back(socket);
|
||||
} else {
|
||||
auto reply = ReplyType::STOP_WAIT;
|
||||
VLOG(7) << "TCPStore: wait reply (" << static_cast<int>(reply)
|
||||
<< ") for key (" << key << ").";
|
||||
tcputils::send_value<ReplyType>(socket, reply);
|
||||
}
|
||||
}
|
||||
|
||||
void MasterDaemon::ProcessCommands(std::vector<struct pollfd>* p_fds) {
|
||||
std::vector<struct pollfd>& fds = *p_fds;
|
||||
// FIXME(gongwb): Don't loop all fds of set just the fds who have event.
|
||||
#ifdef _WIN32
|
||||
// 0: listen socket, so loop from 1.
|
||||
for (size_t i = 1; i < fds.size(); i++) {
|
||||
#else
|
||||
// 0: listen socket, 1:controller pipe, so loop from 2.
|
||||
for (uint i = 2; i < fds.size(); i++) {
|
||||
#endif
|
||||
try {
|
||||
if (fds[i].revents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// VLOG(8) << "Plan to receive command from " << GetSockName(fds[i].fd);
|
||||
Command command = tcputils::receive_value<Command>(fds[i].fd);
|
||||
// VLOG(7) << "TCPStore: recv command: " << static_cast<int>(command) <<
|
||||
// ".";
|
||||
|
||||
switch (command) {
|
||||
case Command::ADD:
|
||||
_do_add(fds[i].fd);
|
||||
break;
|
||||
case Command::GET:
|
||||
_do_get(fds[i].fd);
|
||||
break;
|
||||
case Command::CHECK:
|
||||
_do_check(fds[i].fd);
|
||||
break;
|
||||
case Command::SET:
|
||||
_do_set(fds[i].fd);
|
||||
break;
|
||||
case Command::WAIT:
|
||||
_do_wait(fds[i].fd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// VLOG(8) << "Unknown command: " << static_cast<int>(command)
|
||||
// << " from addr info:" << GetSockName(fds[i].fd);
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
auto map_iter = _waiting_sockets.begin();
|
||||
while (map_iter != _waiting_sockets.end()) {
|
||||
auto vec_iter = map_iter->second.begin();
|
||||
while (vec_iter != map_iter->second.end()) {
|
||||
if (*vec_iter == fds[i].fd) {
|
||||
vec_iter = map_iter->second.erase(vec_iter);
|
||||
} else {
|
||||
++vec_iter;
|
||||
}
|
||||
}
|
||||
if (map_iter->second.empty()) {
|
||||
map_iter = _waiting_sockets.erase(map_iter);
|
||||
} else {
|
||||
++map_iter;
|
||||
}
|
||||
}
|
||||
|
||||
tcputils::close_socket(fds[i].fd);
|
||||
fds.erase(fds.begin() + i);
|
||||
#ifdef _WIN32
|
||||
_sockets.erase(_sockets.begin() + i - 1);
|
||||
#else
|
||||
_sockets.erase(_sockets.begin() + i - 2);
|
||||
#endif
|
||||
std::string s(ex.what());
|
||||
// if (s.find("TCP connection reset by peer") != std::string::npos) {
|
||||
// VLOG(5) << "TCP connection reset by peer";
|
||||
// } else {
|
||||
// VLOG(5) << "Meet some exceptions during run:" << ex.what();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MasterDaemon::run() {
|
||||
std::vector<struct pollfd> fds;
|
||||
#ifdef _WIN32
|
||||
fds.push_back({_listen_socket, POLLIN});
|
||||
#else
|
||||
fds.push_back({.fd = _listen_socket, .events = POLLIN, .revents = 0});
|
||||
fds.push_back(
|
||||
{.fd = _control_fd[0], .events = POLLIN | POLLHUP, .revents = 0});
|
||||
#endif
|
||||
|
||||
bool finished = false;
|
||||
while (!finished) {
|
||||
for (auto& item : fds) {
|
||||
item.revents = 0;
|
||||
}
|
||||
|
||||
VLOG(9) << "begin to poll fds_size:"
|
||||
<< paddle::string::Sprintf("%d", fds.size());
|
||||
#ifdef _WIN32
|
||||
int res = ::WSAPoll(fds.data(), fds.size(), INFTIME);
|
||||
if (res == 0) {
|
||||
auto rv = WaitForSingleObject(ghStopEvent_, 0);
|
||||
if (rv != WAIT_TIMEOUT) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
::poll(fds.data(), fds.size(), INFTIME);
|
||||
|
||||
VLOG(9) << "begin to fds[1].revents:"
|
||||
<< paddle::string::Sprintf("%d", fds[1].revents);
|
||||
// The control pipe receive shutdown event, and begin to close it.
|
||||
if (fds[1].revents != 0) {
|
||||
if (fds[1].revents & ~(POLLIN | POLLHUP)) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Fatal("Undefined event type:%d", fds[1].revents));
|
||||
}
|
||||
VLOG(0)
|
||||
<< "receive shutdown event and so quit from MasterDaemon run loop";
|
||||
finished = true; // NOLINT
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// accept connect request.
|
||||
if (fds[0].revents != 0) {
|
||||
auto socket = tcputils::tcp_accept(_listen_socket);
|
||||
_sockets.emplace_back(socket);
|
||||
#ifdef _WIN32
|
||||
fds.push_back({socket, POLLIN});
|
||||
#else
|
||||
fds.push_back({.fd = socket, .events = POLLIN, .revents = 0});
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcessCommands(&fds);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<TCPServer> TCPServer::create(uint16_t port,
|
||||
int nranks,
|
||||
int stop_check_timeout,
|
||||
bool use_libuv) {
|
||||
auto server = std::make_unique<TCPServer>();
|
||||
if (use_libuv) {
|
||||
// start libuv server
|
||||
VLOG(0) << "create libuv server at port: " << port;
|
||||
server->_master_daemon = create_libuv_tcpstore(port);
|
||||
server->_master_daemon->start();
|
||||
} else {
|
||||
int socket = tcputils::tcp_listen("", std::to_string(port), AF_INET);
|
||||
server->_master_daemon =
|
||||
MasterDaemon::createDaemon(socket, nranks, stop_check_timeout);
|
||||
server->_master_daemon->start();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
std::unique_ptr<TCPClient> TCPClient::connect(const std::string host,
|
||||
uint16_t port) {
|
||||
int socket = tcputils::tcp_connect(host, std::to_string(port), AF_INET);
|
||||
return std::make_unique<TCPClient>(socket);
|
||||
}
|
||||
|
||||
void TCPClient::send_command_for_key(Command type, const std::string& key) {
|
||||
tcputils::send_value<Command>(_socket, type);
|
||||
if (key.empty()) {
|
||||
return;
|
||||
}
|
||||
tcputils::send_string(_socket, key);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TCPClient::send_value(const T& value) {
|
||||
tcputils::send_bytes<T>(_socket, &value, 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T TCPClient::receive_value() {
|
||||
T res;
|
||||
tcputils::receive_bytes<T>(_socket, &res, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TCPClient::send_vector(const std::vector<T>& value) {
|
||||
tcputils::send_vector<T>(_socket, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> TCPClient::receive_vector() {
|
||||
return tcputils::receive_vector<T>(_socket);
|
||||
}
|
||||
|
||||
} // namespace phi::distributed::detail
|
||||
namespace phi::distributed {
|
||||
|
||||
TCPStore::TCPStore(std::string host,
|
||||
uint16_t port,
|
||||
bool is_master,
|
||||
size_t num_workers,
|
||||
int timeout)
|
||||
: Store(timeout),
|
||||
_is_master(is_master),
|
||||
_num_workers(static_cast<int>(num_workers)) {
|
||||
_timeout = timeout;
|
||||
PADDLE_ENFORCE_GT(
|
||||
timeout,
|
||||
0,
|
||||
common::errors::InvalidArgument("timeout must >= %d", timeout));
|
||||
|
||||
VLOG(7) << "input timeout" << timeout << ", member timeout:" << _timeout;
|
||||
if (_is_master) {
|
||||
_server = detail::TCPServer::create(
|
||||
port, this->_num_workers, timeout, FLAGS_tcp_store_using_libuv);
|
||||
}
|
||||
|
||||
_client = detail::TCPClient::connect(host, port);
|
||||
waitWorkers();
|
||||
}
|
||||
|
||||
void TCPStore::waitWorkers() {
|
||||
if (_num_workers == 0) {
|
||||
return;
|
||||
}
|
||||
add(_init_key, 1);
|
||||
|
||||
if (_is_master) {
|
||||
VLOG(7) << paddle::string::Sprintf("_timeout:%d", _timeout);
|
||||
auto begin = std::chrono::steady_clock::now();
|
||||
do {
|
||||
auto value = get(_init_key);
|
||||
int completed = std::stoi(std::string(value.begin(), value.end()));
|
||||
VLOG(7) << completed << " worker ready, total " << _num_workers
|
||||
<< ", _timeout:" << _timeout;
|
||||
if (completed >= _num_workers) {
|
||||
break;
|
||||
}
|
||||
const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::steady_clock::now() - begin);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
if (_timeout != 0 && elapsed.count() > _timeout) {
|
||||
PADDLE_THROW(common::errors::Fatal(paddle::string::Sprintf(
|
||||
"_timeout:%d elapsed:%d (elapsed > _timeout)=%d",
|
||||
_timeout,
|
||||
elapsed.count(),
|
||||
elapsed.count() > _timeout)));
|
||||
|
||||
PADDLE_ENFORCE_EQ(
|
||||
completed,
|
||||
_num_workers,
|
||||
common::errors::InvalidArgument(
|
||||
"TCPStore timeouted and not all workers got ready."));
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
VLOG(7) << "TCPStore initialized.";
|
||||
}
|
||||
|
||||
int64_t TCPStore::add(const std::string& key, int64_t value) {
|
||||
VLOG(7) << "TCPStore add.";
|
||||
_client->send_command_for_key(Command::ADD, _key_prefix + key);
|
||||
_client->send_value<std::int64_t>(value);
|
||||
return _client->receive_value<std::int64_t>();
|
||||
}
|
||||
|
||||
void TCPStore::set(const std::string& key, const std::vector<uint8_t>& value) {
|
||||
VLOG(7) << "TCPStore set.";
|
||||
_client->send_command_for_key(Command::SET, _key_prefix + key);
|
||||
_client->send_vector<uint8_t>(value);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> TCPStore::get(const std::string& key) {
|
||||
wait(key);
|
||||
_client->send_command_for_key(Command::GET, _key_prefix + key);
|
||||
VLOG(7) << "TCPStore get.";
|
||||
return _client->receive_vector<uint8_t>();
|
||||
}
|
||||
|
||||
bool TCPStore::check(const std::string& key) {
|
||||
_client->send_command_for_key(Command::CHECK, _key_prefix + key);
|
||||
VLOG(3) << "TCPStore check.";
|
||||
auto response = _client->receive_value<ReplyType>();
|
||||
if (response == ReplyType::READY) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TCPStore::wait(const std::string& key) {
|
||||
ReplyType reply; // NOLINT
|
||||
VLOG(7) << "TCPStore wait.";
|
||||
_client->send_command_for_key(Command::WAIT, _key_prefix + key);
|
||||
reply = _client->receive_value<ReplyType>();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
reply == ReplyType::STOP_WAIT,
|
||||
true,
|
||||
common::errors::InvalidArgument("Stop_waiting response is expected"));
|
||||
}
|
||||
|
||||
TCPStore::~TCPStore() { VLOG(7) << "TCPStore destructure"; }
|
||||
|
||||
} // namespace phi::distributed
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2021 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "paddle/phi/core/distributed/store/socket.h"
|
||||
#include "paddle/phi/core/distributed/store/store.h"
|
||||
#include "paddle/phi/core/distributed/store/tcp_utils.h"
|
||||
|
||||
namespace phi {
|
||||
namespace distributed {
|
||||
|
||||
enum class ReplyType { WAITING, STOP_WAIT, READY, NOT_READY };
|
||||
enum class Command { ADD, GET, CHECK, SET, WAIT, STOP };
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Abstract base class to handle thread state for TCPStoreMasterDaemon.
|
||||
// Contains the windows/unix implementations to signal a
|
||||
// shutdown sequence for the thread
|
||||
class DaemonThread {
|
||||
public:
|
||||
DaemonThread() = default;
|
||||
virtual ~DaemonThread() = 0;
|
||||
void start();
|
||||
|
||||
protected:
|
||||
void cleanup();
|
||||
virtual void run() = 0;
|
||||
virtual void stop() = 0;
|
||||
bool is_running();
|
||||
|
||||
private:
|
||||
std::atomic<bool> is_running_{false};
|
||||
std::thread daemonThread_{};
|
||||
};
|
||||
|
||||
std::unique_ptr<DaemonThread> create_libuv_tcpstore(const std::uint16_t& port);
|
||||
|
||||
class MasterDaemon : public DaemonThread {
|
||||
public:
|
||||
static std::unique_ptr<MasterDaemon> createDaemon(SocketType listen_socket,
|
||||
int nranks,
|
||||
int timeout);
|
||||
MasterDaemon() = delete;
|
||||
explicit MasterDaemon(SocketType listen_socket,
|
||||
int nranks,
|
||||
int stop_check_timeout);
|
||||
~MasterDaemon() override;
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
void stop() override{};
|
||||
|
||||
private:
|
||||
void ProcessCommands(std::vector<struct pollfd>* p_fds);
|
||||
void _do_add(SocketType socket);
|
||||
void _do_wait(SocketType socket);
|
||||
void _do_get(SocketType socket);
|
||||
void _do_check(SocketType socket);
|
||||
void _do_set(SocketType socket);
|
||||
void _notify_waiting_sockets(const std::string&);
|
||||
SocketType _listen_socket;
|
||||
std::vector<SocketType> _sockets;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> _store;
|
||||
std::thread _background_thread{};
|
||||
int _nranks = -1;
|
||||
int _timeout = 0;
|
||||
std::unordered_map<std::string, std::vector<SocketType>>
|
||||
_waiting_sockets; // key -> list of waiting sockets
|
||||
|
||||
void InitControlFd();
|
||||
void CloseControlFd();
|
||||
void StopByControlFd();
|
||||
#ifdef _WIN32
|
||||
HANDLE ghStopEvent_{};
|
||||
#else
|
||||
std::array<int, 2> _control_fd{{-1, -1}};
|
||||
#endif
|
||||
};
|
||||
|
||||
class TCPServer {
|
||||
public:
|
||||
TCPServer() = default;
|
||||
static std::unique_ptr<TCPServer> create(std::uint16_t port,
|
||||
int nranks,
|
||||
int stop_check_timeout,
|
||||
bool use_libuv);
|
||||
|
||||
private:
|
||||
std::unique_ptr<DaemonThread> _master_daemon;
|
||||
};
|
||||
|
||||
class TCPClient {
|
||||
public:
|
||||
explicit TCPClient(SocketType socket) : _socket{socket} {}
|
||||
static std::unique_ptr<TCPClient> connect(const std::string host,
|
||||
uint16_t port);
|
||||
~TCPClient() { tcputils::close_socket(_socket); }
|
||||
void send_command_for_key(Command type, const std::string& key);
|
||||
|
||||
template <typename T>
|
||||
void send_value(const T& value);
|
||||
|
||||
template <typename T>
|
||||
void send_vector(const std::vector<T>& value);
|
||||
template <typename T>
|
||||
std::vector<T> receive_vector();
|
||||
|
||||
template <typename T>
|
||||
T receive_value();
|
||||
|
||||
private:
|
||||
SocketType _socket;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// TODO(gongwb) :Add IP6 support.
|
||||
class PADDLE_API TCPStore : public Store {
|
||||
public:
|
||||
static constexpr std::uint16_t kDefaultPort = 6170;
|
||||
explicit TCPStore(std::string host,
|
||||
uint16_t port = kDefaultPort,
|
||||
bool is_master = false,
|
||||
size_t num_workers = 1,
|
||||
int timeout = 900);
|
||||
|
||||
~TCPStore();
|
||||
|
||||
int64_t add(const std::string& key, int64_t value) override;
|
||||
std::vector<uint8_t> get(const std::string& key) override;
|
||||
bool check(const std::string& key) override;
|
||||
void wait(const std::string& key) override;
|
||||
void set(const std::string& key, const std::vector<uint8_t>& value) override;
|
||||
|
||||
private:
|
||||
void waitWorkers();
|
||||
std::unique_ptr<detail::TCPServer> _server;
|
||||
std::unique_ptr<detail::TCPClient> _client;
|
||||
|
||||
const std::string _init_key = "init/";
|
||||
const std::string _key_prefix = "/";
|
||||
|
||||
bool _is_master;
|
||||
int _num_workers;
|
||||
};
|
||||
|
||||
} // namespace distributed
|
||||
} // namespace phi
|
||||
@@ -0,0 +1,738 @@
|
||||
// Copyright (c) 2025 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 "glog/logging.h"
|
||||
|
||||
#include "paddle/phi/core/distributed/store/tcp_store_libuv.h"
|
||||
|
||||
namespace phi::distributed::detail {
|
||||
|
||||
// SegmentedDataStream
|
||||
void SegmentedDataStream::append(uv_buf_t buf) {
|
||||
if (buf.len == 0) {
|
||||
free(buf.base);
|
||||
} else {
|
||||
capacity += buf.len;
|
||||
_buffers.push_back(buf);
|
||||
}
|
||||
}
|
||||
|
||||
bool SegmentedDataStream::readMany(char* dest, size_t size) {
|
||||
if (available() < size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t remaining = size;
|
||||
char* write_base = dest;
|
||||
while (remaining > 0) {
|
||||
auto to_read = std::min(_buffers[_buff_idx].len - _buff_offset, remaining);
|
||||
::memcpy(write_base, _buffers[_buff_idx].base + _buff_offset, to_read);
|
||||
_buff_offset += to_read;
|
||||
remaining -= to_read;
|
||||
write_base += to_read;
|
||||
if (_buff_offset >= _buffers[_buff_idx].len) {
|
||||
_buff_offset = 0;
|
||||
++_buff_idx;
|
||||
if (_buff_idx >= _buffers.size() && remaining > 0) {
|
||||
PADDLE_THROW(common::errors::Fatal(paddle::string::Sprintf(
|
||||
"Read operation exceeds buffer boundary. ",
|
||||
"buffer index: %d, available: %d, remaining: %d",
|
||||
_buff_idx,
|
||||
_buffers.size(),
|
||||
remaining)));
|
||||
}
|
||||
}
|
||||
}
|
||||
_read_offset += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SegmentedDataStream::readValue(T& value) {
|
||||
return readMany(reinterpret_cast<char*>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
bool SegmentedDataStream::readKey(std::string& str) {
|
||||
uint64_t size = 0;
|
||||
if (!readValue(size)) return false;
|
||||
PADDLE_ENFORCE_LE(size,
|
||||
phi::distributed::detail::MAX_KEY_LEN,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"Key size validation failed. size: %d, max: %d",
|
||||
size,
|
||||
phi::distributed::detail::MAX_KEY_LEN)));
|
||||
|
||||
if (available() < size) return false;
|
||||
str.resize(size);
|
||||
return readMany(reinterpret_cast<char*>(str.data()), size);
|
||||
}
|
||||
|
||||
bool SegmentedDataStream::readContent(std::vector<uint8_t>& data) {
|
||||
uint64_t size = 0;
|
||||
if (!readValue(size)) return false;
|
||||
auto size_in_bytes = size * sizeof(uint8_t);
|
||||
PADDLE_ENFORCE_LE(size_in_bytes,
|
||||
MAX_CONTENT_LEN,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"Content size validation failed. size: %d, max: %d",
|
||||
size_in_bytes,
|
||||
MAX_CONTENT_LEN)));
|
||||
|
||||
if (available() < size_in_bytes) return false;
|
||||
data.resize(size);
|
||||
return readMany(reinterpret_cast<char*>(data.data()), size_in_bytes);
|
||||
}
|
||||
|
||||
size_t SegmentedDataStream::available() { return capacity - _read_offset; }
|
||||
|
||||
void SegmentedDataStream::commit() {
|
||||
if (_buff_idx >= _buffers.size() || _buff_offset >= _buffers[_buff_idx].len) {
|
||||
_buff_offset = 0;
|
||||
if (_buff_idx < _buffers.size()) ++_buff_idx;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < _buff_idx; ++i) {
|
||||
free(_buffers[0].base);
|
||||
capacity -= _buffers[0].len;
|
||||
_buffers.pop_front();
|
||||
}
|
||||
_buff_idx = 0;
|
||||
_read_offset = _buff_offset_commit = _buff_offset;
|
||||
}
|
||||
|
||||
void SegmentedDataStream::reset() {
|
||||
_buff_idx = 0;
|
||||
_read_offset = _buff_offset = _buff_offset_commit;
|
||||
}
|
||||
|
||||
// LibUVHandle
|
||||
std::shared_ptr<LibUVHandle> LibUVHandle::ptr() { return shared_from_this(); }
|
||||
|
||||
void LibUVHandle::close() {
|
||||
if (uv_is_closing(getRawHandle())) {
|
||||
return;
|
||||
}
|
||||
uv_close(getRawHandle(), handleClose);
|
||||
}
|
||||
|
||||
void LibUVHandle::handleAvailable() {
|
||||
uv_handle_set_data(getRawHandle(), this);
|
||||
}
|
||||
|
||||
void LibUVHandle::handleClose(uv_handle_t* uv_handle) {
|
||||
auto h = reinterpret_cast<LibUVHandle*>(uv_handle_get_data(uv_handle));
|
||||
h->onClose();
|
||||
}
|
||||
|
||||
// ==== LibUVTCPSocket ====
|
||||
LibUVTCPSocket::LibUVTCPSocket(uv_loop_t* loop) {
|
||||
uv_tcp_init(loop, &client);
|
||||
if (int err = uv_tcp_nodelay(&client, 1)) {
|
||||
VLOG(2) << "The no-delay option is unavailable. err: " << err;
|
||||
}
|
||||
}
|
||||
|
||||
uv_handle_t* LibUVTCPSocket::getRawHandle() {
|
||||
return reinterpret_cast<uv_handle_t*>(&client);
|
||||
}
|
||||
|
||||
std::shared_ptr<LibUVTCPSocket> LibUVTCPSocket::ptr() {
|
||||
return std::static_pointer_cast<LibUVTCPSocket>(shared_from_this());
|
||||
}
|
||||
|
||||
std::shared_ptr<LibUVTCPSocket> LibUVTCPSocket::getTCPSocket(
|
||||
uv_stream_t* handle) {
|
||||
auto h = reinterpret_cast<LibUVTCPSocket*>(
|
||||
uv_handle_get_data(reinterpret_cast<uv_handle_t*>(handle)));
|
||||
return h->ptr();
|
||||
}
|
||||
|
||||
// LibUVTCPServer
|
||||
void LibUVTCPServer::setCallback(LibUVCallback&& callback) {
|
||||
_on_connect_callback = std::move(callback);
|
||||
}
|
||||
|
||||
std::shared_ptr<LibUVTCPServer> LibUVTCPServer::createServer(uv_loop_t* loop,
|
||||
std::uint16_t port,
|
||||
bool useIpv6) {
|
||||
auto res = std::make_shared<LibUVTCPServer>(loop);
|
||||
res->handleAvailable();
|
||||
try {
|
||||
struct sockaddr_storage addr {};
|
||||
int uv_res = 0;
|
||||
if (useIpv6) {
|
||||
uv_res = uv_ip6_addr("::", port, (struct sockaddr_in6*)&addr);
|
||||
} else {
|
||||
uv_res = uv_ip4_addr("0.0.0.0", port, (struct sockaddr_in*)&addr);
|
||||
}
|
||||
PADDLE_ENFORCE_EQ(uv_res,
|
||||
0,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"sockaddr parsing failure. port: %d, useIpv6:%d, "
|
||||
"code: %d, name: %s, message: %s",
|
||||
port,
|
||||
useIpv6,
|
||||
uv_res,
|
||||
uv_err_name(uv_res),
|
||||
uv_strerror(uv_res))));
|
||||
|
||||
uv_res =
|
||||
uv_tcp_bind(res->getRawSocket(), (const struct ::sockaddr*)&addr, 0);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
uv_res,
|
||||
0,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"Bind operation failed for the server socket. port: %d, "
|
||||
"useIpv6: %d, code: %d, name: %s, message: %s",
|
||||
port,
|
||||
useIpv6,
|
||||
uv_res,
|
||||
uv_err_name(uv_res),
|
||||
uv_strerror(uv_res))));
|
||||
|
||||
uv_res = uv_listen(
|
||||
res->getRawStream(), FLAGS_tcp_max_syn_backlog, onNewConnection);
|
||||
PADDLE_ENFORCE_EQ(
|
||||
uv_res,
|
||||
0,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"Server socket unable to listen on local network interfaces. "
|
||||
"port: %d, useIpv6: %d, code: %d, name: %s, message: %s",
|
||||
port,
|
||||
useIpv6,
|
||||
uv_res,
|
||||
uv_err_name(uv_res),
|
||||
uv_strerror(uv_res))));
|
||||
res->setSocketPort();
|
||||
} catch (std::exception& ex) {
|
||||
res->close();
|
||||
throw;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void LibUVTCPServer::accept(const std::shared_ptr<LibUVTCPSocket>& socket) {
|
||||
int res = uv_accept(getRawStream(),
|
||||
reinterpret_cast<uv_stream_t*>(socket->getRawHandle()));
|
||||
PADDLE_ENFORCE_EQ(
|
||||
res,
|
||||
0,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"Socket accept operation failed. code: %d, name: %s, message: %s",
|
||||
res,
|
||||
uv_err_name(res),
|
||||
uv_strerror(res))));
|
||||
}
|
||||
|
||||
void LibUVTCPServer::setSocketPort() {
|
||||
sockaddr_storage addr_s{};
|
||||
int addr_len = sizeof(addr_s);
|
||||
if (uv_tcp_getsockname(reinterpret_cast<uv_tcp_t*>(getRawStream()),
|
||||
reinterpret_cast<::sockaddr*>(&addr_s),
|
||||
&addr_len) != 0) {
|
||||
throw std::runtime_error("the port number cannot be retrieved.");
|
||||
}
|
||||
if (addr_s.ss_family == AF_INET) {
|
||||
_port = ntohs(reinterpret_cast<sockaddr_in*>(&addr_s)->sin_port);
|
||||
} else {
|
||||
_port = ntohs(reinterpret_cast<sockaddr_in6*>(&addr_s)->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVTCPServer::onNewConnection(uv_stream_t* server, int status) {
|
||||
auto h = reinterpret_cast<LibUVTCPServer*>(
|
||||
uv_handle_get_data(reinterpret_cast<uv_handle_t*>(server)));
|
||||
h->_on_connect_callback(status);
|
||||
}
|
||||
|
||||
// WriteUVContent
|
||||
WriteUVContent::WriteUVContent(std::vector<uint8_t>&& in_data,
|
||||
std::shared_ptr<LibUVHandle> handle)
|
||||
: data(std::move(in_data)), handle(std::move(handle)) {
|
||||
uv_req_set_data(reinterpret_cast<uv_req_t*>(&req), new RequestData());
|
||||
}
|
||||
|
||||
void WriteUVContent::writeDone(uv_write_t* req, int status) {
|
||||
auto data_ptr = static_cast<RequestData*>(
|
||||
uv_req_get_data(reinterpret_cast<uv_req_t*>(req)));
|
||||
if (!data_ptr) return;
|
||||
|
||||
auto self = std::move(data_ptr->strong_self);
|
||||
delete data_ptr;
|
||||
uv_req_set_data(reinterpret_cast<uv_req_t*>(req), nullptr);
|
||||
if (self && status) {
|
||||
VLOG(2) << "Write to client failed. code:" << status
|
||||
<< " desc:" << uv_strerror(status)
|
||||
<< " name:" << uv_err_name(status);
|
||||
self->handle->close();
|
||||
}
|
||||
}
|
||||
|
||||
WriteUVContent::~WriteUVContent() {
|
||||
// safely clean up pending request data
|
||||
if (auto data = static_cast<RequestData*>(
|
||||
uv_req_get_data(reinterpret_cast<uv_req_t*>(&req)))) {
|
||||
delete data;
|
||||
uv_req_set_data(reinterpret_cast<uv_req_t*>(&req), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteUVContent::send() {
|
||||
if (data.empty()) return;
|
||||
buf = uv_buf_init(reinterpret_cast<char*>(data.data()), data.size());
|
||||
int res = uv_write(&req,
|
||||
reinterpret_cast<uv_stream_t*>(handle->getRawHandle()),
|
||||
&buf,
|
||||
1,
|
||||
writeDone);
|
||||
if (res) {
|
||||
VLOG(2) << "Write failed. code:" << res << " desc:" << uv_strerror(res)
|
||||
<< " name:" << uv_err_name(res);
|
||||
handle->close();
|
||||
} else {
|
||||
auto data_ptr = static_cast<RequestData*>(
|
||||
uv_req_get_data(reinterpret_cast<uv_req_t*>(&req)));
|
||||
if (data_ptr) {
|
||||
data_ptr->strong_self = shared_from_this();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UVWriter
|
||||
template <typename T>
|
||||
void UVWriter::writeValue(T val) {
|
||||
uint8_t* val_ptr = reinterpret_cast<uint8_t*>(&val);
|
||||
data.insert(data.end(), val_ptr, val_ptr + sizeof(T));
|
||||
}
|
||||
|
||||
void UVWriter::writeVector(const std::vector<uint8_t>& val) {
|
||||
writeValue<uint64_t>(val.size());
|
||||
data.insert(data.end(), val.begin(), val.end());
|
||||
}
|
||||
|
||||
void UVWriter::writeString(const std::string& val) {
|
||||
writeValue<uint64_t>(val.size());
|
||||
data.insert(data.end(), val.data(), val.data() + val.size());
|
||||
}
|
||||
|
||||
void UVWriter::send() {
|
||||
auto wd = std::make_shared<WriteUVContent>(std::move(data), handle);
|
||||
wd->send();
|
||||
}
|
||||
|
||||
// LibUVClient
|
||||
void LibUVClient::allocBuffer(uv_handle_t* handle,
|
||||
size_t buf_size,
|
||||
uv_buf_t* buf) {
|
||||
buf_size = std::min(buf_size, MAX_BUFFER_SIZE);
|
||||
buf->base = reinterpret_cast<char*>(malloc(buf_size));
|
||||
buf->len = buf_size;
|
||||
}
|
||||
|
||||
void LibUVClient::readCallback(uv_stream_t* client,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf) {
|
||||
auto uv_socket = LibUVTCPSocket::getTCPSocket(client);
|
||||
if (nread > 0) {
|
||||
try {
|
||||
uv_socket->doProcess(buf, nread);
|
||||
return;
|
||||
} catch (std::exception& ex) {
|
||||
VLOG(2) << "Failed to process incoming client message: " << ex.what();
|
||||
uv_socket->close();
|
||||
}
|
||||
} else if (nread == UV_EOF) {
|
||||
// EOF
|
||||
VLOG(5) << "Remote peer closed the connection.";
|
||||
uv_socket->close();
|
||||
} else if (nread < 0) {
|
||||
// error and EOF
|
||||
VLOG(5) << "Read callback handler exception. code:" << nread
|
||||
<< " desc:" << uv_strerror(nread) << " name:" << uv_err_name(nread);
|
||||
uv_socket->close();
|
||||
}
|
||||
free(buf->base);
|
||||
}
|
||||
|
||||
void LibUVClient::doProcess(const uv_buf_t* buf, size_t nread) {
|
||||
auto tmp = *buf;
|
||||
tmp.len = nread;
|
||||
stream.append(tmp);
|
||||
|
||||
VLOG(5) << "process: " << std::string(buf->base, nread)
|
||||
<< ", nread: " << nread;
|
||||
while (true) {
|
||||
stream.reset();
|
||||
uint32_t command = -1;
|
||||
if (!stream.readValue(command)) break;
|
||||
|
||||
VLOG(5) << "Client parse command" << command;
|
||||
switch ((Command)command) {
|
||||
case Command::ADD:
|
||||
if (!doAddCommand()) return;
|
||||
break;
|
||||
case Command::GET:
|
||||
if (!doGetCommand()) return;
|
||||
break;
|
||||
case Command::CHECK:
|
||||
if (!doCheckCommand()) return;
|
||||
break;
|
||||
case Command::SET:
|
||||
if (!doSetCommand()) return;
|
||||
break;
|
||||
case Command::WAIT:
|
||||
if (!doWaitCommand()) return;
|
||||
break;
|
||||
default:
|
||||
VLOG(4) << "invalid command from Client, command: " << command;
|
||||
close();
|
||||
return;
|
||||
}
|
||||
stream.commit();
|
||||
}
|
||||
}
|
||||
|
||||
bool LibUVClient::doSetCommand() {
|
||||
std::string key;
|
||||
if (!stream.readKey(key)) return false;
|
||||
|
||||
std::vector<uint8_t> newData;
|
||||
if (!stream.readContent(newData)) return false;
|
||||
VLOG(7) << "set key:" << key << " address:" << this->address();
|
||||
store->set(key, newData);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibUVClient::doGetCommand() {
|
||||
std::string key;
|
||||
if (!stream.readKey(key)) return false;
|
||||
|
||||
VLOG(7) << "get key: " << key << " address:" << this->address();
|
||||
const auto& data = store->get(key);
|
||||
UVWriter sw(ptr());
|
||||
sw.writeVector(data);
|
||||
sw.send();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibUVClient::doAddCommand() {
|
||||
std::string key;
|
||||
if (!stream.readKey(key)) return false;
|
||||
int64_t addVal = 0;
|
||||
if (!stream.readValue(addVal)) return false;
|
||||
|
||||
addVal = store->add(key, addVal);
|
||||
VLOG(7) << "add key:" << key << " val: " << addVal
|
||||
<< " address:" << this->address();
|
||||
UVWriter sw(ptr());
|
||||
sw.writeValue(addVal);
|
||||
sw.send();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibUVClient::doCheckCommand() {
|
||||
std::string key;
|
||||
if (!stream.readKey(key)) return false;
|
||||
|
||||
VLOG(7) << "check key:" << key << " address:" << this->address();
|
||||
std::vector<std::string> keys = {key};
|
||||
UVWriter sw(ptr());
|
||||
if (store->checkKeys(keys)) {
|
||||
sw.writeValue(ReplyType::READY);
|
||||
} else {
|
||||
sw.writeValue(ReplyType::NOT_READY);
|
||||
}
|
||||
sw.send();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibUVClient::doWaitCommand() {
|
||||
std::string key;
|
||||
if (!stream.readKey(key)) return false;
|
||||
|
||||
VLOG(7) << "wait key: " << key << " address:" << this->address();
|
||||
if (store->waitKey(key, ptr())) {
|
||||
UVWriter sw(ptr());
|
||||
sw.writeValue(ReplyType::STOP_WAIT);
|
||||
sw.send();
|
||||
VLOG(7) << "wait send: " << key;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibUVClient::onClose() { store->removeClient(ptr()); }
|
||||
|
||||
PADDLE_API std::string fmtSockAddr(const struct ::sockaddr* addr,
|
||||
socklen_t len) {
|
||||
char host[NI_MAXHOST], port[NI_MAXSERV]; // NOLINT
|
||||
int flags = NI_NUMERICSERV;
|
||||
int err =
|
||||
::getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), flags);
|
||||
if (err) {
|
||||
VLOG(1) << "Cannot resolve hostname, fallback to numeric. Error: " << err;
|
||||
// fallback to numeric
|
||||
flags |= NI_NUMERICHOST;
|
||||
err =
|
||||
::getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), flags);
|
||||
if (err) {
|
||||
VLOG(1) << "Numeric address resolution failed. Error: " << err;
|
||||
return "?UNKNOWN?";
|
||||
}
|
||||
}
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
return paddle::string::Sprintf("%s:%s", host, port);
|
||||
case AF_INET6:
|
||||
return paddle::string::Sprintf("[%s]:%s", host, port);
|
||||
default:
|
||||
return paddle::string::Sprintf("[%s]:%s", host, port);
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVClient::readStart() {
|
||||
struct ::sockaddr_storage addr {};
|
||||
int addrLen{sizeof(struct ::sockaddr_storage)};
|
||||
|
||||
if (int err = uv_tcp_getpeername(
|
||||
&client, reinterpret_cast<struct ::sockaddr*>(&addr), &addrLen)) {
|
||||
VLOG(2) << "Remote endpoint resolution failed. err=" << uv_strerror(err);
|
||||
} else {
|
||||
_address =
|
||||
fmtSockAddr(reinterpret_cast<struct ::sockaddr*>(&addr), addrLen);
|
||||
}
|
||||
int res = uv_read_start(
|
||||
reinterpret_cast<uv_stream_t*>(&client), allocBuffer, readCallback);
|
||||
if (res) {
|
||||
VLOG(2) << "Read callback initialization failure. client:"
|
||||
<< reinterpret_cast<void*>(this) << " code:" << res
|
||||
<< " desc:" << uv_strerror(res) << " name:" << uv_err_name(res);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<LibUVClient> LibUVClient::make(uv_loop_t* loop,
|
||||
LibUVMasterDaemon* store) {
|
||||
auto res = std::make_shared<LibUVClient>(loop, store);
|
||||
res->handleAvailable();
|
||||
return res;
|
||||
}
|
||||
|
||||
std::shared_ptr<LibUVClient> LibUVClient::ptr() {
|
||||
return std::static_pointer_cast<LibUVClient>(shared_from_this());
|
||||
}
|
||||
|
||||
// LibUVMasterDaemon
|
||||
void LibUVMasterDaemon::onConnect(int status) {
|
||||
auto client = LibUVClient::make(&loop_, this);
|
||||
addClient(client);
|
||||
try {
|
||||
_tcp_server->accept(client);
|
||||
client->readStart();
|
||||
} catch (std::exception& e) {
|
||||
VLOG(2) << "Accept client failed, err: " << e.what();
|
||||
client->close();
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::onExitRequest() {
|
||||
VLOG(4) << "begin to exit requested";
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&_exit_handle), nullptr);
|
||||
uv_stop(&loop_);
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::init(const std::uint16_t& port) {
|
||||
try {
|
||||
_tcp_server = LibUVTCPServer::createServer(&loop_, port, /*useIpv6=*/false);
|
||||
} catch (std::exception& ex) {
|
||||
PADDLE_THROW(common::errors::Fatal(
|
||||
paddle::string::Sprintf("Bind to ipv4 address failed: %s", ex.what())));
|
||||
}
|
||||
_tcp_server->setCallback([this](auto status) { this->onConnect(status); });
|
||||
|
||||
port_ = _tcp_server->port();
|
||||
PADDLE_ENFORCE_EQ(
|
||||
port_,
|
||||
port,
|
||||
common::errors::InvalidArgument(paddle::string::Sprintf(
|
||||
"listen fd is bound to port %d, but expected port %d", port_, port)));
|
||||
}
|
||||
|
||||
LibUVMasterDaemon::LibUVMasterDaemon(int port) : port_(port) {
|
||||
// uv loop init
|
||||
PADDLE_ENFORCE_EQ(uv_loop_init(&loop_),
|
||||
0,
|
||||
common::errors::InvalidArgument("init libuv loop failed"));
|
||||
// uv async init
|
||||
PADDLE_ENFORCE_EQ(
|
||||
uv_async_init(&loop_, &_exit_handle, LibUVMasterDaemon::on_exit_request),
|
||||
0,
|
||||
common::errors::InvalidArgument("init libuv async event failed"));
|
||||
uv_handle_set_data(reinterpret_cast<uv_handle_t*>(&_exit_handle), this);
|
||||
}
|
||||
|
||||
LibUVMasterDaemon::~LibUVMasterDaemon() {
|
||||
if (!is_running()) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&_exit_handle), nullptr);
|
||||
uv_run(&loop_, UV_RUN_NOWAIT);
|
||||
if (uv_loop_close(&loop_) != 0) {
|
||||
VLOG(0) << "uv loop close failed";
|
||||
}
|
||||
} else {
|
||||
// the daemon thread cleanup libuv
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::run() {
|
||||
VLOG(4) << "start LibUV master daemon loop";
|
||||
int res = uv_run(&loop_, UV_RUN_DEFAULT);
|
||||
if (res) {
|
||||
VLOG(4) << "LibUV master daemon loop done: " << res;
|
||||
}
|
||||
|
||||
for (const auto& client : _clients) {
|
||||
client->close();
|
||||
}
|
||||
_tcp_server->close();
|
||||
|
||||
while (true) {
|
||||
res = uv_loop_close(&loop_);
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
VLOG(3) << "uv_loop_close failed with:" << res
|
||||
<< " err: " << uv_err_name(res)
|
||||
<< " std error:" << uv_strerror(res);
|
||||
res = uv_run(&loop_, UV_RUN_NOWAIT);
|
||||
if (res != 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
}
|
||||
VLOG(3) << "LibUV master daemon loop cleanup finished.";
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::stop() {
|
||||
int res = uv_async_send(&_exit_handle);
|
||||
if (res) {
|
||||
VLOG(2) << "stop with uv_async_send failed:" << res
|
||||
<< " err:" << uv_err_name(res) << " std error:" << uv_strerror(res);
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::addClient(const std::shared_ptr<LibUVHandle>& client) {
|
||||
_clients.insert(client);
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::removeClient(
|
||||
const std::shared_ptr<LibUVHandle>& client) {
|
||||
_clients.erase(client);
|
||||
clearWaitState(client);
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::clearWaitState(
|
||||
const std::shared_ptr<LibUVHandle>& client) {
|
||||
if (_awaited_keys.find(client) == _awaited_keys.end()) {
|
||||
return;
|
||||
}
|
||||
_awaited_keys.erase(client);
|
||||
for (auto it = _waiting_sockets.begin(); it != _waiting_sockets.end();) {
|
||||
for (auto vecIt = it->second.begin(); vecIt != it->second.end();) {
|
||||
if (*vecIt == client) {
|
||||
vecIt = it->second.erase(vecIt);
|
||||
} else {
|
||||
++vecIt;
|
||||
}
|
||||
}
|
||||
if (it->second.empty()) {
|
||||
it = _waiting_sockets.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::set(const std::string& key,
|
||||
const std::vector<uint8_t>& value) {
|
||||
_tcp_store[key] = value;
|
||||
// notify all clients that have been waiting
|
||||
notifyWaitingClients(key);
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& LibUVMasterDaemon::get(const std::string& key) {
|
||||
static std::vector<uint8_t> missing_key;
|
||||
return _tcp_store.count(key) ? _tcp_store.at(key) : missing_key;
|
||||
}
|
||||
|
||||
int64_t LibUVMasterDaemon::add(const std::string& key, int64_t addVal) {
|
||||
std::vector<uint8_t> old_data;
|
||||
auto it = _tcp_store.find(key);
|
||||
if (it != _tcp_store.end()) {
|
||||
old_data = it->second;
|
||||
auto buf = reinterpret_cast<const char*>(it->second.data());
|
||||
auto len = it->second.size();
|
||||
addVal += std::stoll(std::string(buf, len));
|
||||
}
|
||||
auto addValStr = std::to_string(addVal);
|
||||
std::vector<uint8_t> newData =
|
||||
std::vector<uint8_t>(addValStr.begin(), addValStr.end());
|
||||
_tcp_store[key] = newData;
|
||||
|
||||
// notify all clients that have been waiting
|
||||
notifyWaitingClients(key);
|
||||
return addVal;
|
||||
}
|
||||
|
||||
bool LibUVMasterDaemon::checkKeys(const std::vector<std::string>& keys) {
|
||||
return std::all_of(keys.begin(), keys.end(), [&](const std::string& s) {
|
||||
if (_tcp_store.count(s) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool LibUVMasterDaemon::waitKey(const std::string& key,
|
||||
const std::shared_ptr<LibUVHandle>& client) {
|
||||
int num_to_await = 0;
|
||||
if (_tcp_store.find(key) == _tcp_store.end()) {
|
||||
_waiting_sockets[key].push_back(client);
|
||||
num_to_await++;
|
||||
VLOG(7) << "add to wait key: " << key;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
_awaited_keys[client] = num_to_await;
|
||||
return false;
|
||||
}
|
||||
|
||||
void LibUVMasterDaemon::notifyWaitingClients(const std::string& key) {
|
||||
auto sockets_to_wait = _waiting_sockets.find(key);
|
||||
if (sockets_to_wait != _waiting_sockets.end()) {
|
||||
for (const auto& client : sockets_to_wait->second) {
|
||||
if (--_awaited_keys[client] == 0) {
|
||||
UVWriter sw(client->ptr());
|
||||
sw.writeValue(ReplyType::STOP_WAIT);
|
||||
sw.send();
|
||||
}
|
||||
}
|
||||
_waiting_sockets.erase(sockets_to_wait);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<phi::distributed::detail::DaemonThread> create_libuv_tcpstore(
|
||||
const std::uint16_t& port) {
|
||||
auto res = std::make_unique<LibUVMasterDaemon>(port);
|
||||
res->init(port);
|
||||
return res;
|
||||
}
|
||||
} // namespace phi::distributed::detail
|
||||
@@ -0,0 +1,242 @@
|
||||
// Copyright (c) 2025 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 <uv.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <deque>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "paddle/common/flags.h"
|
||||
#include "paddle/common/macros.h"
|
||||
#include "paddle/phi/core/distributed/store/tcp_store.h"
|
||||
#include "paddle/phi/core/distributed/store/tcp_utils.h"
|
||||
|
||||
namespace phi::distributed::detail {
|
||||
auto constexpr MAX_KEY_LEN = 16 * 1024;
|
||||
auto constexpr MAX_CONTENT_LEN = 16 * 1024 * 1024;
|
||||
auto constexpr MAX_BUFFER_SIZE = size_t(4096);
|
||||
|
||||
class PADDLE_API SegmentedDataStream {
|
||||
std::deque<uv_buf_t> _buffers;
|
||||
size_t _buff_idx{0};
|
||||
size_t _buff_offset{0};
|
||||
size_t capacity{0};
|
||||
size_t _buff_offset_commit{0};
|
||||
size_t _read_offset{0};
|
||||
|
||||
public:
|
||||
SegmentedDataStream() = default;
|
||||
void append(uv_buf_t buf);
|
||||
bool readMany(char* dest, size_t size);
|
||||
template <typename T>
|
||||
bool readValue(T& value); // NOLINT(runtime/references)
|
||||
|
||||
bool readKey(std::string& str); // NOLINT(runtime/references)
|
||||
bool readContent(std::vector<uint8_t>& data); // NOLINT(runtime/references)
|
||||
size_t available();
|
||||
void commit();
|
||||
void reset();
|
||||
};
|
||||
|
||||
class PADDLE_API LibUVHandle
|
||||
: public std::enable_shared_from_this<LibUVHandle> {
|
||||
public:
|
||||
~LibUVHandle() = default;
|
||||
std::shared_ptr<LibUVHandle> ptr();
|
||||
virtual uv_handle_t* getRawHandle() = 0;
|
||||
void close();
|
||||
|
||||
protected:
|
||||
void handleAvailable();
|
||||
virtual void onClose() = 0;
|
||||
|
||||
private:
|
||||
static void handleClose(uv_handle_t* uv_handle);
|
||||
};
|
||||
|
||||
class PADDLE_API LibUVTCPSocket : public LibUVHandle {
|
||||
public:
|
||||
explicit LibUVTCPSocket(uv_loop_t* loop);
|
||||
uv_handle_t* getRawHandle() override;
|
||||
std::shared_ptr<LibUVTCPSocket> ptr();
|
||||
static std::shared_ptr<LibUVTCPSocket> getTCPSocket(uv_stream_t* handle);
|
||||
virtual void doProcess(const uv_buf_t* buf, size_t nread) {
|
||||
PADDLE_THROW(
|
||||
common::errors::Fatal("Socket subclass does not implement doProcess"));
|
||||
}
|
||||
uv_tcp_t client{};
|
||||
|
||||
protected:
|
||||
void onClose() override {}
|
||||
};
|
||||
|
||||
class PADDLE_API LibUVTCPServer : public LibUVTCPSocket {
|
||||
public:
|
||||
typedef std::function<void(int)> LibUVCallback;
|
||||
explicit LibUVTCPServer(uv_loop_t* loop)
|
||||
: LibUVTCPSocket(loop), _on_connect_callback(defaultOnConnect) {}
|
||||
void setCallback(LibUVCallback&& callback);
|
||||
static std::shared_ptr<LibUVTCPServer> createServer(uv_loop_t* loop,
|
||||
std::uint16_t port,
|
||||
bool useIpv6);
|
||||
std::uint16_t port() const { return _port; }
|
||||
void accept(const std::shared_ptr<LibUVTCPSocket>& socket);
|
||||
|
||||
protected:
|
||||
uv_tcp_t* getRawSocket() { return &client; }
|
||||
uv_stream_t* getRawStream() {
|
||||
return reinterpret_cast<uv_stream_t*>(&client);
|
||||
}
|
||||
|
||||
private:
|
||||
LibUVCallback _on_connect_callback;
|
||||
std::uint16_t _port{};
|
||||
|
||||
void setSocketPort();
|
||||
static void defaultOnConnect(int status) {
|
||||
PADDLE_THROW(common::errors::Fatal(
|
||||
"Socket accepted, but onConnect callback is undefined"));
|
||||
}
|
||||
static void onNewConnection(uv_stream_t* server, int status);
|
||||
};
|
||||
|
||||
class PADDLE_API LibUVMasterDaemon : public DaemonThread {
|
||||
public:
|
||||
explicit LibUVMasterDaemon(int port);
|
||||
// Disable copy constructor
|
||||
LibUVMasterDaemon(const LibUVMasterDaemon& other) = delete;
|
||||
// Disable move constructor
|
||||
LibUVMasterDaemon(LibUVMasterDaemon&& other) = delete;
|
||||
// Disable copy assignment operator
|
||||
LibUVMasterDaemon& operator=(const LibUVMasterDaemon& other) = delete;
|
||||
// Disable move assignment operator
|
||||
LibUVMasterDaemon& operator=(LibUVMasterDaemon&& other) = delete;
|
||||
~LibUVMasterDaemon() override;
|
||||
void init(const std::uint16_t& port);
|
||||
// operator for key
|
||||
void set(const std::string& key, const std::vector<uint8_t>& value);
|
||||
const std::vector<uint8_t>& get(const std::string& key);
|
||||
int64_t add(const std::string& key, int64_t addVal);
|
||||
bool waitKey(const std::string& key,
|
||||
const std::shared_ptr<LibUVHandle>& client);
|
||||
bool checkKeys(const std::vector<std::string>& keys);
|
||||
// client
|
||||
void addClient(const std::shared_ptr<LibUVHandle>& client);
|
||||
void removeClient(const std::shared_ptr<LibUVHandle>& client);
|
||||
void clearWaitState(const std::shared_ptr<LibUVHandle>& client);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
uv_loop_t loop_{};
|
||||
uv_async_t _exit_handle{};
|
||||
// tcp server
|
||||
std::shared_ptr<LibUVTCPServer> _tcp_server;
|
||||
// tcp store
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> _tcp_store;
|
||||
// the list of LibUVClient waiting on the key
|
||||
std::unordered_map<std::string, std::vector<std::shared_ptr<LibUVHandle>>>
|
||||
_waiting_sockets;
|
||||
// number of keys awaited
|
||||
std::unordered_map<std::shared_ptr<LibUVHandle>, size_t> _awaited_keys;
|
||||
std::unordered_set<std::shared_ptr<LibUVHandle>> _clients;
|
||||
int port_;
|
||||
|
||||
static LibUVMasterDaemon& UVMasterDaemon(uv_handle_t* stream) {
|
||||
return *reinterpret_cast<LibUVMasterDaemon*>(uv_handle_get_data(stream));
|
||||
}
|
||||
static void on_new_connection(uv_stream_t* server, int status) {
|
||||
UVMasterDaemon(reinterpret_cast<uv_handle_t*>(server)).onConnect(status);
|
||||
}
|
||||
static void on_exit_request(uv_async_t* handle) {
|
||||
UVMasterDaemon(reinterpret_cast<uv_handle_t*>(handle)).onExitRequest();
|
||||
}
|
||||
void onConnect(int status);
|
||||
void onExitRequest();
|
||||
void notifyWaitingClients(const std::string& key);
|
||||
};
|
||||
|
||||
class PADDLE_API WriteUVContent
|
||||
: public std::enable_shared_from_this<WriteUVContent> {
|
||||
std::shared_ptr<WriteUVContent> ptr() { return shared_from_this(); }
|
||||
static void writeDone(uv_write_t* req, int status);
|
||||
struct RequestData {
|
||||
std::shared_ptr<WriteUVContent> strong_self;
|
||||
};
|
||||
std::vector<uint8_t> data;
|
||||
uv_write_t req = {};
|
||||
uv_buf_t buf = {};
|
||||
std::shared_ptr<LibUVHandle> handle;
|
||||
|
||||
public:
|
||||
WriteUVContent(std::vector<uint8_t>&& in_data,
|
||||
std::shared_ptr<LibUVHandle> handle);
|
||||
~WriteUVContent();
|
||||
void send();
|
||||
};
|
||||
|
||||
class PADDLE_API UVWriter {
|
||||
std::vector<uint8_t> data;
|
||||
std::shared_ptr<LibUVHandle> handle;
|
||||
void* operator new(size_t);
|
||||
|
||||
public:
|
||||
explicit UVWriter(std::shared_ptr<LibUVHandle> handle)
|
||||
: handle(std::move(handle)) {}
|
||||
template <typename T>
|
||||
void writeValue(T val);
|
||||
void writeVector(const std::vector<uint8_t>& val);
|
||||
void writeString(const std::string& val);
|
||||
void send();
|
||||
};
|
||||
|
||||
class PADDLE_API LibUVClient : public LibUVTCPSocket {
|
||||
SegmentedDataStream stream;
|
||||
LibUVMasterDaemon* store;
|
||||
std::string _address{"null"};
|
||||
const std::string& address() const { return _address; }
|
||||
static void allocBuffer(uv_handle_t* handle, size_t buf_size, uv_buf_t* buf);
|
||||
static void readCallback(uv_stream_t* client,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf);
|
||||
|
||||
protected:
|
||||
void doProcess(const uv_buf_t* buf, size_t nread) override;
|
||||
bool doSetCommand();
|
||||
bool doGetCommand();
|
||||
bool doAddCommand();
|
||||
bool doCheckCommand();
|
||||
bool doWaitCommand();
|
||||
void onClose() override;
|
||||
|
||||
public:
|
||||
explicit LibUVClient(uv_loop_t* loop, LibUVMasterDaemon* store)
|
||||
: LibUVTCPSocket(loop), store(store) {}
|
||||
void readStart();
|
||||
static std::shared_ptr<LibUVClient> make(uv_loop_t* loop,
|
||||
LibUVMasterDaemon* store);
|
||||
std::shared_ptr<LibUVClient> ptr();
|
||||
};
|
||||
} // namespace phi::distributed::detail
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user