This feature tracks the proxy events and status of each send/recv op. ProxyTrace keeps a fixed number of active ops in host mem and dumps the status of each op when the program crashes or hangs.

[ROCm/rccl commit: 020dcf0a7c]
This commit is contained in:
Dingming Wu
2025-06-25 21:01:34 -07:00
committed by GitHub
szülő 0c41f27b10
commit d34a38ccfc
11 fájl változott, egészen pontosan 693 új sor hozzáadva és 2 régi sor törölve
@@ -0,0 +1,264 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "proxy_trace/proxy_trace.h"
#include "debug.h"
#include "device.h"
#include "proxy.h"
#include <fmt/format.h>
#include <map>
constexpr int32_t kFinishedProxyOpItems = 32;
static std::unordered_map<facebook_rccl::ProxyOpStepStatus, std::string>
proxyStepStatusStrMap = {
{facebook_rccl::ProxyOpStepStatus::INIT, "INIT"},
{facebook_rccl::ProxyOpStepStatus::POSTING, "POSTING"},
{facebook_rccl::ProxyOpStepStatus::SENDING, "SENDING"},
{facebook_rccl::ProxyOpStepStatus::RECEIVING, "RECEIVING"},
{facebook_rccl::ProxyOpStepStatus::WAITING_GPU, "WAITING_GPU"},
{facebook_rccl::ProxyOpStepStatus::FLUSHING, "FLUSHING"},
{facebook_rccl::ProxyOpStepStatus::DONE, "DONE"},
{facebook_rccl::ProxyOpStepStatus::UNINITIALIZED, "ILLEGAL"},
};
void facebook_rccl::ProxyTrace::resetAll() {
activeOps.clear();
activeOpIdTracker.clear();
myRank = -1;
initialized = false;
}
bool facebook_rccl::ProxyTrace::checkActiveOpExist(uint64_t commHash,
uint64_t opCount,
uint32_t proxyOpId) const {
return (activeOps.find(commHash) != activeOps.end() &&
activeOps.at(commHash).find(opCount) !=
activeOps.at(commHash).end() &&
activeOps.at(commHash).at(opCount).find(proxyOpId) !=
activeOps.at(commHash).at(opCount).end());
}
// Get a unique proxyOpId for a given commHash:opCount
// If the opCount is not found, create a new entry for it and return 0
int64_t facebook_rccl::ProxyTrace::getOrCreateProxyOpId(uint64_t commHash,
uint64_t opCount) {
return activeOpIdTracker[commHash][opCount];
}
facebook_rccl::ProxyTraceOp *
facebook_rccl::ProxyTrace::getProxyTraceOpPtr(const ProxyTraceRecordKey &key) {
if (checkActiveOpExist(key.commHash, key.opCount, key.proxyOpId)) {
return &(activeOps.at(key.commHash).at(key.opCount).at(key.proxyOpId));
}
return nullptr;
}
void facebook_rccl::ProxyTrace::checkOpCompleted(
const ProxyTraceRecordKey &key) {
if (checkActiveOpExist(key.commHash, key.opCount, key.proxyOpId)) {
auto &traceOp = activeOps[key.commHash][key.opCount][key.proxyOpId];
// Remove finished proxyOp or colls to avoid memory leak
if (traceOp.counters[facebook_rccl::ProxyCounterTypes::DONE] ==
traceOp.nSteps) {
traceOp.status = ProxyOpStepStatus::DONE;
if (finishedOps.size() >= kFinishedProxyOpItems) {
finishedOps.pop_front();
}
finishedOps.push_back({key.str(), traceOp.str()});
activeOps[key.commHash][key.opCount].erase(key.proxyOpId);
if (activeOps[key.commHash][key.opCount].empty()) {
activeOps[key.commHash].erase(key.opCount);
activeOpIdTracker[key.commHash].erase(key.opCount);
}
}
} else {
WARN("[proxyTrace] ProxyTraceOp %s not found", key.str().c_str());
}
}
void facebook_rccl::ProxyTrace::addNewProxyTraceOpImpl(
const ProxyTraceRecordKey &key, const ProxyTraceExtraInfo &extraInfo,
ProxyOpType opType, int channelId, int nSteps, uint32_t nbytes,
int peerRank) {
if (nSteps > 0 &&
!checkActiveOpExist(key.commHash, key.opCount, key.proxyOpId)) {
auto traceOp = facebook_rccl::ProxyTraceOp();
traceOp.opType = opType;
traceOp.traceKey = key;
traceOp.extraInfo = extraInfo;
traceOp.channelId = channelId;
traceOp.nSteps = nSteps;
traceOp.nbytes = nbytes;
traceOp.myRank = this->myRank;
traceOp.peerRank = peerRank;
traceOp.startTs = std::chrono::high_resolution_clock::now();
traceOp.status = ProxyOpStepStatus::INIT;
activeOpIdTracker[key.commHash][key.opCount]++;
activeOps[key.commHash][key.opCount].emplace(key.proxyOpId,
std::move(traceOp));
} else if (nSteps == 0) {
INFO(NCCL_PROXY, "nSteps is 0, ignored %s", key.str().c_str());
}
}
void facebook_rccl::ProxyTraceOp::computeStatus() {
ProxyOpStepStatus newStatus;
int posted = counters[facebook_rccl::ProxyCounterTypes::POSTED];
int received = counters[facebook_rccl::ProxyCounterTypes::RECEIVED];
int transmitted = counters[facebook_rccl::ProxyCounterTypes::TRANSMITTED];
int done = counters[facebook_rccl::ProxyCounterTypes::DONE];
if (opType == ProxyOpType::RECV) {
if (posted < nSteps && posted < done + NCCL_STEPS)
newStatus = ProxyOpStepStatus::POSTING; // Init
else if (received < posted)
newStatus = ProxyOpStepStatus::RECEIVING; // Receiving
else if (received < transmitted)
newStatus = ProxyOpStepStatus::RECEIVING; // Receiving
else if (transmitted < received)
newStatus = ProxyOpStepStatus::FLUSHING; // Flushing
else if (done < transmitted)
newStatus = ProxyOpStepStatus::WAITING_GPU; // Waiting on GPU
else
newStatus = ProxyOpStepStatus::DONE; // Done
} else {
if (posted < nSteps && posted < done + NCCL_STEPS)
newStatus = ProxyOpStepStatus::POSTING; // Init
else if (transmitted < posted)
newStatus = ProxyOpStepStatus::WAITING_GPU; // Waiting on GPU
else if (done < transmitted)
newStatus = ProxyOpStepStatus::SENDING; // Sending
else
newStatus = ProxyOpStepStatus::DONE; // Done
}
this->status = newStatus;
}
std::string facebook_rccl::ProxyTrace::dump(uint64_t commHash) {
std::string result = fmt::format("commDump for commHash:{}\n", commHash);
std::map<std::string, std::string> sortedDumpStrMap;
for (auto &opCountMap : activeOps.at(commHash)) {
for (auto &proxyOpMap : opCountMap.second) {
ProxyTraceRecordKey traceKey = {commHash, opCountMap.first,
proxyOpMap.first};
proxyOpMap.second.computeStatus();
sortedDumpStrMap[traceKey.str()] = proxyOpMap.second.str();
}
}
for (const auto &[keyStr, proxyOpStr] : sortedDumpStrMap) {
result += proxyOpStr;
}
return result;
}
std::string facebook_rccl::ProxyTrace::dump() {
std::string result = "commDump for all active ops ";
result += fmt::format("mapSizeMB:{:.2f}\n", getMapSizeMB());
// maps serialized key to serliazed proxyOp; sorted by key
std::map<std::string, std::string> sortedDumpStrMap;
for (auto &[commHash, opCountMap] : activeOps) {
for (auto &[opCount, proxyOpMap] : opCountMap) {
for (auto &[opId, opEntry] : proxyOpMap) {
ProxyTraceRecordKey traceKey = {commHash, opCount, opId};
opEntry.computeStatus();
sortedDumpStrMap[traceKey.str()] = opEntry.str();
}
}
}
// add the recent finished ops as well
for (const auto &[keyStr, proxyOpStr] : finishedOps) {
sortedDumpStrMap[keyStr] = proxyOpStr;
}
for (const auto &[keyStr, proxyOpStr] : sortedDumpStrMap) {
result += proxyOpStr;
}
return result;
}
std::string facebook_rccl::ProxyTraceOp::str() {
computeStatus();
std::string ret = fmt::format(
"createT:{}, lastT:{}, cntNm:{}, {}, {}, {}->{}({}), "
"chan:{}, status:{}, ns:{}, nb:{}, po:{}, ke:{}, tail/h:{}, recvT:{}, "
"connSz/h:{}, trans:{}, flushed:{}, recvd:{}, done:{}\n",
std::chrono::duration_cast<std::chrono::milliseconds>(
startTs.time_since_epoch())
.count(),
std::chrono::duration_cast<std::chrono::milliseconds>(
lastUpdateTs.time_since_epoch())
.count(),
static_cast<int>(lastUpdatingCounter), traceKey.str(), extraInfo.str(),
myRank, peerRank, opType == ProxyOpType::SEND ? "S" : "R", channelId,
proxyStepStatusStrMap[status], nSteps, nbytes,
counters[ProxyCounterTypes::POSTED],
counters[ProxyCounterTypes::KERNEL_COPY_READY],
counters[ProxyCounterTypes::TAIL_OR_HEAD],
counters[ProxyCounterTypes::RECV_TAIL],
counters[ProxyCounterTypes::FIFO_SZ_OR_HEAD_CACHE],
counters[ProxyCounterTypes::TRANSMITTED],
counters[ProxyCounterTypes::FLUSHED],
counters[ProxyCounterTypes::RECEIVED], counters[ProxyCounterTypes::DONE]);
return ret;
}
float facebook_rccl::ProxyTrace::getMapSizeMB() const {
float size = 0;
for (const auto &[commHash, opCountMap] : activeOps) {
for (const auto &[opCount, proxyOpMap] : opCountMap) {
size += proxyOpMap.size() *
(sizeof(ProxyTraceOp) +
sizeof(std::unique_ptr<facebook_rccl::ProxyTraceOp>));
}
}
for (const auto &[keyStr, proxyOpStr] : finishedOps) {
size += keyStr.size() + proxyOpStr.size();
}
return size / 1024.0 / 1024.0;
}
void facebook_rccl::proxyTraceInit(std::unique_ptr<ProxyTrace> &proxyTrace,
int32_t rank, uint64_t commHash) {
if (proxyTrace) {
WARN("[proxyTrace] Initializing non-empty proxyTrace! rank: %d, commHash: "
"%lu",
rank, commHash);
return;
}
INFO(NCCL_PROXY, "Initializing ProxyTrace, rank: %d, commHash: %lu", rank,
commHash);
proxyTrace = std::make_unique<facebook_rccl::ProxyTrace>(rank);
proxyTrace->initialized = true;
}
void facebook_rccl::updateProxyOpCounter(
std::unique_ptr<ProxyTrace> &proxyTraceObj,
const ProxyTraceRecordKey &traceKey, ProxyCounterTypes counter,
int64_t val) {
if (proxyTraceObj) {
auto traceOpPtr = proxyTraceObj->getProxyTraceOpPtr(traceKey);
if (traceOpPtr) {
traceOpPtr->counters[counter] = val;
traceOpPtr->lastUpdateTs = std::chrono::high_resolution_clock::now();
traceOpPtr->lastUpdatingCounter = counter;
proxyTraceObj->checkOpCompleted(traceKey);
}
}
}
void facebook_rccl::addNewProxyOp(std::unique_ptr<ProxyTrace> &proxyTraceObj,
ProxyTraceRecordKey &key,
const ProxyTraceExtraInfo &extraInfo,
ProxyOpType opType, int channelId, int nSteps,
uint32_t nbytes, int peerRank) {
if (proxyTraceObj) {
auto opId = proxyTraceObj->getOrCreateProxyOpId(key.commHash, key.opCount);
key.proxyOpId = opId;
proxyTraceObj->addNewProxyTraceOpImpl(key, extraInfo, opType, channelId,
nSteps, nbytes, peerRank);
}
}