Files
paddlepaddle--paddle/paddle/fluid/imperative/heter_ccl_context.cc
T
2026-07-13 12:40:42 +08:00

200 lines
6.6 KiB
C++

// 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.
#include "paddle/fluid/imperative/heter_ccl_context.h"
// NCCL first
#ifdef PADDLE_WITH_NCCL
#include "paddle/fluid/imperative/all_reduce.h"
#endif
#include "paddle/fluid/framework/fleet/gloo_wrapper.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/platform/collective_helper.h"
#include "paddle/phi/core/platform/device_context.h"
#include "paddle/phi/core/platform/gen_comm_id_helper.h"
#include "paddle/utils/string/split.h"
#include "paddle/utils/string/string_helper.h"
namespace paddle {
namespace framework {
class Variable;
} // namespace framework
} // namespace paddle
namespace paddle {
namespace imperative {
HeterParallelContext::HeterParallelContext(const ParallelStrategy &strategy,
const int &device_id)
#ifdef PADDLE_WITH_NCCL
: ParallelContext(strategy, phi::GPUPlace(device_id))
#elif PADDLE_WITH_XPU_BKCL
: ParallelContext(strategy, phi::XPUPlace(device_id))
#else
: ParallelContext(strategy, CPUPlace())
#endif
{
// construct node_strategy_ from global strategy by selecting the
// endpoints with same ip address.
std::string node_ip = strategy_.current_endpoint_.substr(
0, strategy_.current_endpoint_.find(':'));
int node_nranks = 0;
int inter_rank = -1;
std::vector<std::string> all_eps = strategy_.trainer_endpoints_;
std::vector<std::string> inter_endpoints;
std::set<std::string> nodes_ips;
for (auto ep : all_eps) {
std::string ip = ep.substr(0, ep.find(':'));
// record ip of different nodes
if (nodes_ips.find(ip) == nodes_ips.end()) {
if (ep == strategy_.current_endpoint_) {
inter_rank = nodes_ips.size();
}
inter_endpoints.push_back(ep);
nodes_ips.emplace(ip);
}
if (ip == node_ip) {
if (ep == strategy_.current_endpoint_) {
node_strategy_.local_rank_ = node_nranks;
}
node_nranks++;
node_strategy_.trainer_endpoints_.push_back(ep);
}
}
VLOG(0) << "init node size " << node_nranks << " rank "
<< node_strategy_.local_rank_;
PADDLE_ENFORCE_NE(node_nranks,
0,
common::errors::InvalidArgument(
"The number of local nranks should not be zero."));
node_strategy_.nranks_ = node_nranks;
node_strategy_.current_endpoint_ = strategy_.current_endpoint_;
if (inter_rank >= 0 && inter_endpoints.size() > 1) {
inter_strategy_.nranks_ = inter_endpoints.size();
inter_strategy_.local_rank_ = inter_rank;
inter_strategy_.current_endpoint_ = strategy_.current_endpoint_;
inter_strategy_.trainer_endpoints_ = inter_endpoints;
#ifdef PADDLE_WITH_GLOO
inter_parallel_ctx_ =
std::make_shared<GLOOParallelContext>(inter_strategy_, CPUPlace());
#endif
}
VLOG(0) << "init inter size " << inter_endpoints.size() << " rank "
<< inter_rank;
#ifdef PADDLE_WITH_NCCL
node_place_ = phi::GPUPlace(device_id);
node_parallel_ctx_ =
std::make_shared<NCCLParallelContext>(node_strategy_, node_place_);
#endif
#ifdef PADDLE_WITH_XPU_BKCL
node_place_ = phi::XPUPlace(device_id);
node_parallel_ctx_ =
std::make_shared<BKCLParallelContext>(node_strategy_, node_place_);
#endif
}
void HeterParallelContext::Init() {
PADDLE_ENFORCE_NE(
node_parallel_ctx_,
nullptr,
common::errors::Unavailable(
"The heter parallel context has not been initialized."));
if (inter_parallel_ctx_ != nullptr) {
inter_parallel_ctx_->Init();
}
node_parallel_ctx_->Init();
VLOG(3) << "/// DEBUG /// heter parallel env init done..." << std::endl;
}
void HeterParallelContext::InitWithRingID(int ring_id) {
PADDLE_THROW(common::errors::Unimplemented(
"Unimplemented InitWithRingID from heter ctx."));
}
void HeterParallelContext::AllReduceByStream(const framework::Variable &src,
framework::Variable *dst,
int ring_id,
bool use_calc_stream) {
// step 1: call reduce within node
VLOG(3) << "/// DEBUG /// step 1: reduce in node... ";
node_parallel_ctx_->AllReduceByStream(src, dst, ring_id, false);
node_parallel_ctx_->WaitComm(ring_id);
// step 2: call allreduce between nodes with gloo
if (inter_parallel_ctx_ != nullptr) {
// copy src to cpu
// dst is now the src
auto src_tensor = dst->Get<DenseTensor>();
framework::Variable src_cpu;
auto src_cpu_tensor = src_cpu.GetMutable<DenseTensor>();
framework::TensorCopySync(src_tensor, CPUPlace(), src_cpu_tensor);
// allreduce src/cpu to dst/cpu
framework::Variable dst_cpu;
inter_parallel_ctx_->AllReduceByStream(src_cpu, &dst_cpu, ring_id, false);
inter_parallel_ctx_->WaitComm(ring_id);
// copy dst/cpu to dst
auto dst_cpu_tensor = dst_cpu.Get<DenseTensor>();
auto dst_tensor = dst->GetMutable<DenseTensor>();
framework::TensorCopySync(dst_cpu_tensor, dst_tensor->place(), dst_tensor);
inter_parallel_ctx_->WaitComm(ring_id);
}
// step 3: call broadcast within node
VLOG(3) << "/// DEBUG /// step 3: broadcast within node... ";
node_parallel_ctx_->WaitComm(ring_id);
node_parallel_ctx_->Broadcast(dst, ring_id);
node_parallel_ctx_->WaitComm(ring_id);
}
void HeterParallelContext::Broadcast(framework::Variable *src, int ring_id) {
PADDLE_THROW(common::errors::Unimplemented("Unimplemented function."));
}
phi::DeviceContext *HeterParallelContext::GetDeviceContext(int ring_id) {
// directly call the implementation of target parallel ctx.
return node_parallel_ctx_->GetDeviceContext(ring_id);
}
void HeterParallelContext::WaitCompute(int ring_id) {
// directly call the implementation of target parallel ctx.
node_parallel_ctx_->WaitCompute(ring_id);
}
void HeterParallelContext::WaitComm(int ring_id) {
// directly call the implementation of target parallel ctx.
node_parallel_ctx_->WaitComm(ring_id);
}
void HeterParallelContext::SynchronizeCompute() {
// directly call the implementation of target parallel ctx.
node_parallel_ctx_->SynchronizeCompute();
}
} // namespace imperative
} // namespace paddle