NCCL 2.26.2-1
Profiler improvements * Add events for CUDA kernel start and end. * Allow network plugins to generate profiling events * Enable profiling on a per-operation basis, rather than per-communicator. * Add support for graph capturing. Add implicit launch order * Allow to prevent deadlocks when using multiple NCCL communicators per device by implicitly ordering NCCL operations using the host program order. Disabled by default, set NCCL_LAUNCH_ORDER_IMPLICIT=1 to enable. * Add a complementary mechanism to detect host threads racing to launch to the same device. Enabled by default, set NCCL_LAUNCH_RACE_FATAL=0 to disable. Optimize the PAT algorithm * Separate the computation and execution of PAT steps on different warps, allowing to run up to 16 PAT steps in parallel to significantly accelerate PAT and reduce its linear part. Add support for setting QoS per communicator * Add a new trafficClass field to the communicator configuration, to allow the application to select a particular traffic class for a given communicator. The meaning of the traffic class is network-specific and should be set in accordance with the network configuration. * For the IB/RoCE plugin, existing config variables such as NCCL_IB_SL and NCCL_IB_TC take precedence. Allow to enable GPU Direct RDMA specifically on C2C platforms * Disabled by default, set NCCL_NET_GDR_C2C=1 to enable. Do not disable user buffer registration unless PXN is really used * Only disable UB when a communicator has more than one rank per node on any node. RAS subsystem improvements * Report operation counts separately for each collective operation type. * Provide details about missing communicator ranks and reliably distinguish ranks that are no longer a given communicator's members (now reported as NOCOMM) from those that failed to respond. Add support for timestamps to NCCL diagnostic messages * On by default for WARN messages; NCCL_DEBUG_TIMESTAMP_LEVELS can be used to enable them for other debug levels as well. * The format can be changed using the NCCL_DEBUG_TIMESTAMP_FORMAT config variable. Reduce the memory usage with NVLink SHARP (NVLS) * Potentially save hundreds of MBs of device memory, considering the multicast buffer size granularity separately from the address alignment. Update performance tuning for recent Intel CPUs * Improve algorithm/protocol selection on recent CPUs such as Emerald Rapids and Sapphire Rapids. Improve channel scheduling when mixing LL and Simple operations. * Make LL operations account for 4x more traffic to ensure LL and simple operations complete at the same time. Refactor the plugin code * Clean up and harmonize the support code across the network, tuner, and profiler plugins. Add support for comment lines (starting with #) in the nccl.conf file * Issue #1540. Make user buffer registration problems print an INFO instead of a WARN. Drop support for network plugin interface version 5. Fix a race condition with split-shared communicators * NCCL could hang during connection setup if multiple communicators were grouped together that share resources. Fix a performance regression when using NCCL_CROSS_NIC=1 * NCCL would unnecessarily alternate rings, breaking the GPU-NIC associations. Make GID index detection code more resilient * Dynamic GID detection code was giving up too soon if the detected index was not available (e.g., wasn't mapped to the container's sysfs). * Issues #1538, #1573. Fix a race condition with non-blocking operation * Fix issue when creating a non-blocking communicator after a non- blocking collective operation on another communicator. Fix shared memory usage on recent Blackwell GPUs. * Issues NVIDIA/nccl-tests#287, NVIDIA/nccl-tests#291, #1637. Fix an error with NIC fusion and IB SHARP when recreating communicators * Disable the unloading of network plugins Make the auto-merge failures in the NIC fusion non-fatal * This could happen when trying to merge IB and RoCE devices. Fixes to ncclCommAbort * Fix hangs due to the progress thread spinning indefinitely on the network progress. * Reduce the abort time by up to two orders of magnitude. Fix a crash when libnccl.so was dynamically unloaded * The RAS subsystem was missing a clean-up handler. Fix a hang if the network plugin's test() call returns an error. Fix a hang on heterogeneous architectures * Ensure we harmonize the tuning to avoid different tuning choices, causing a hang. Fix double-free on failed ncclCommInitRank and ncclCommFinalize. Fix a potential list traversal bug during a group launch of multiple communicators * Issue #1599. Unify the handling of NCCL configuration variables * Under rare circumstances, some variables specified in the config file could be ignored.
이 커밋은 다음에 포함됨:
+145
-14
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "nccl_net.h"
|
||||
#include <ctime>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -16,6 +17,11 @@
|
||||
#include "param.h"
|
||||
|
||||
int ncclDebugLevel = -1;
|
||||
static uint32_t ncclDebugTimestampLevels = 0; // bitmaps of levels that have timestamps turned on
|
||||
static char ncclDebugTimestampFormat[256]; // with space for subseconds
|
||||
static int ncclDebugTimestampSubsecondsStart; // index where the subseconds starts
|
||||
static uint64_t ncclDebugTimestampMaxSubseconds; // Max number of subseconds plus 1, used in duration ratio
|
||||
static int ncclDebugTimestampSubsecondDigits; // Number of digits to display
|
||||
static int pid = -1;
|
||||
static char hostname[1024];
|
||||
thread_local int ncclDebugNoWarn = 0;
|
||||
@@ -112,6 +118,84 @@ static void ncclDebugInit() {
|
||||
ncclWarnSetDebugInfo = value;
|
||||
}
|
||||
|
||||
// Determine which debug levels will have timestamps.
|
||||
const char* timestamps = ncclGetEnv("NCCL_DEBUG_TIMESTAMP_LEVELS");
|
||||
if (timestamps == nullptr) {
|
||||
ncclDebugTimestampLevels = (1<<NCCL_LOG_WARN);
|
||||
} else {
|
||||
int invert = 0;
|
||||
if (timestamps[0] == '^') { invert = 1; ++timestamps; }
|
||||
ncclDebugTimestampLevels = invert ? ~0U : 0U;
|
||||
char *timestampsDup = strdup(timestamps);
|
||||
char *level = strtok(timestampsDup, ",");
|
||||
while (level != NULL) {
|
||||
uint32_t mask = 0;
|
||||
if (strcasecmp(level, "ALL") == 0) {
|
||||
mask = ~0U;
|
||||
} else if (strcasecmp(level, "VERSION") == 0) {
|
||||
mask = (1<<NCCL_LOG_VERSION);
|
||||
} else if (strcasecmp(level, "WARN") == 0) {
|
||||
mask = (1<<NCCL_LOG_WARN);
|
||||
} else if (strcasecmp(level, "INFO") == 0) {
|
||||
mask = (1<<NCCL_LOG_INFO);
|
||||
} else if (strcasecmp(level, "ABORT") == 0) {
|
||||
mask = (1<<NCCL_LOG_ABORT);
|
||||
} else if (strcasecmp(level, "TRACE") == 0) {
|
||||
mask = (1<<NCCL_LOG_TRACE);
|
||||
} else {
|
||||
// Silently fail.
|
||||
}
|
||||
if (mask) {
|
||||
if (invert) ncclDebugTimestampLevels &= ~mask;
|
||||
else ncclDebugTimestampLevels |= mask;
|
||||
}
|
||||
level = strtok(NULL, ",");
|
||||
}
|
||||
free(timestampsDup);
|
||||
}
|
||||
|
||||
// Store a copy of the timestamp format with space for the subseconds, if used.
|
||||
const char* tsFormat = ncclGetEnv("NCCL_DEBUG_TIMESTAMP_FORMAT");
|
||||
if (tsFormat == nullptr) tsFormat = "[%F %T] ";
|
||||
ncclDebugTimestampSubsecondsStart = -1;
|
||||
// Find where the subseconds are in the format.
|
||||
for (int i=0; tsFormat[i] != '\0'; ++i) {
|
||||
if (tsFormat[i]=='%' && tsFormat[i+1]=='%') { // Next two chars are "%"
|
||||
// Skip the next character, too, and restart checking after that.
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (tsFormat[i]=='%' && // Found a percentage
|
||||
('1' <= tsFormat[i+1] && tsFormat[i+1] <= '9') && // Next char is a digit between 1 and 9 inclusive
|
||||
tsFormat[i+2]=='f' // Two characters later is an "f"
|
||||
) {
|
||||
constexpr int replaceLen = sizeof("%Xf") - 1;
|
||||
ncclDebugTimestampSubsecondDigits = tsFormat[i+1] - '0';
|
||||
if (ncclDebugTimestampSubsecondDigits + strlen(tsFormat) - replaceLen > sizeof(ncclDebugTimestampFormat) - 1) {
|
||||
// Won't fit; fall back on the default.
|
||||
break;
|
||||
}
|
||||
ncclDebugTimestampSubsecondsStart = i;
|
||||
ncclDebugTimestampMaxSubseconds = 1;
|
||||
|
||||
memcpy(ncclDebugTimestampFormat, tsFormat, i);
|
||||
for (int j=0; j<ncclDebugTimestampSubsecondDigits; ++j) {
|
||||
ncclDebugTimestampFormat[i+j] = ' ';
|
||||
ncclDebugTimestampMaxSubseconds *= 10;
|
||||
}
|
||||
strcpy(ncclDebugTimestampFormat+i+ncclDebugTimestampSubsecondDigits, tsFormat+i+replaceLen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ncclDebugTimestampSubsecondsStart == -1) {
|
||||
if (strlen(tsFormat) < sizeof(ncclDebugTimestampFormat)) {
|
||||
strcpy(ncclDebugTimestampFormat, tsFormat);
|
||||
} else {
|
||||
strcpy(ncclDebugTimestampFormat, "[%F %T] ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Cache pid and hostname
|
||||
getHostName(hostname, 1024, '.');
|
||||
pid = getpid();
|
||||
@@ -192,39 +276,86 @@ void ncclDebugLog(ncclDebugLogLevel level, unsigned long flags, const char *file
|
||||
tid = syscall(SYS_gettid);
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
size_t len = 0;
|
||||
|
||||
// WARNs come with an extra newline at the beginning.
|
||||
if (level == NCCL_LOG_WARN) {
|
||||
buffer[len++] = '\n';
|
||||
};
|
||||
|
||||
// Add the timestamp to the buffer if they are turned on for this level.
|
||||
if (ncclDebugTimestampLevels & (1<<level)) {
|
||||
if (ncclDebugTimestampFormat[0] != '\0') {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts); // clock_gettime failure should never happen
|
||||
std::tm nowTm;
|
||||
localtime_r(&ts.tv_sec, &nowTm);
|
||||
|
||||
// Add the subseconds portion if it is part of the format.
|
||||
char localTimestampFormat[sizeof(ncclDebugTimestampFormat)];
|
||||
const char* pformat = ncclDebugTimestampFormat;
|
||||
if (ncclDebugTimestampSubsecondsStart != -1) {
|
||||
pformat = localTimestampFormat; // Need to use the local version which has subseconds
|
||||
memcpy(localTimestampFormat, ncclDebugTimestampFormat, ncclDebugTimestampSubsecondsStart);
|
||||
snprintf(localTimestampFormat + ncclDebugTimestampSubsecondsStart,
|
||||
ncclDebugTimestampSubsecondDigits+1,
|
||||
"%0*ld", ncclDebugTimestampSubsecondDigits,
|
||||
ts.tv_nsec / (1000000UL/ncclDebugTimestampMaxSubseconds));
|
||||
strcpy( localTimestampFormat+ncclDebugTimestampSubsecondsStart+ncclDebugTimestampSubsecondDigits,
|
||||
ncclDebugTimestampFormat+ncclDebugTimestampSubsecondsStart+ncclDebugTimestampSubsecondDigits);
|
||||
}
|
||||
|
||||
// Format the time. If it runs out of space, fall back on a simpler format.
|
||||
int adv = std::strftime(buffer+len, sizeof(buffer)-len, pformat, &nowTm);
|
||||
if (adv==0 && ncclDebugTimestampFormat[0] != '\0') {
|
||||
// Ran out of space. Fall back on the default. This should never fail.
|
||||
adv = std::strftime(buffer+len, sizeof(buffer)-len, "[%F %T] ", &nowTm);
|
||||
}
|
||||
len += adv;
|
||||
}
|
||||
}
|
||||
len = std::min(len, sizeof(buffer)-1); // prevent overflows
|
||||
|
||||
// Add hostname, pid and tid portion of the log line.
|
||||
if (level != NCCL_LOG_VERSION) {
|
||||
len += snprintf(buffer+len, sizeof(buffer)-len, "%s:%d:%d ", hostname, pid, tid);
|
||||
len = std::min(len, sizeof(buffer)-1); // prevent overflows
|
||||
}
|
||||
|
||||
int cudaDev = 0;
|
||||
if (!(level == NCCL_LOG_TRACE && flags == NCCL_CALL)) {
|
||||
(void)cudaGetDevice(&cudaDev);
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
size_t len = 0;
|
||||
// Add level specific formatting.
|
||||
if (level == NCCL_LOG_WARN) {
|
||||
len = snprintf(buffer, sizeof(buffer), "\n%s:%d:%d [%d] %s:%d NCCL WARN ",
|
||||
hostname, pid, tid, cudaDev, filefunc, line);
|
||||
len += snprintf(buffer+len, sizeof(buffer)-len, "[%d] %s:%d NCCL WARN ", cudaDev, filefunc, line);
|
||||
if (ncclWarnSetDebugInfo) ncclDebugLevel = NCCL_LOG_INFO;
|
||||
} else if (level == NCCL_LOG_INFO) {
|
||||
len = snprintf(buffer, sizeof(buffer), "%s:%d:%d [%d] NCCL INFO ", hostname, pid, tid, cudaDev);
|
||||
len += snprintf(buffer+len, sizeof(buffer)-len, "[%d] NCCL INFO ", cudaDev);
|
||||
} else if (level == NCCL_LOG_TRACE && flags == NCCL_CALL) {
|
||||
len = snprintf(buffer, sizeof(buffer), "%s:%d:%d NCCL CALL ", hostname, pid, tid);
|
||||
len += snprintf(buffer+len, sizeof(buffer)-len, "NCCL CALL ");
|
||||
} else if (level == NCCL_LOG_TRACE) {
|
||||
auto delta = std::chrono::steady_clock::now() - ncclEpoch;
|
||||
double timestamp = std::chrono::duration_cast<std::chrono::duration<double>>(delta).count()*1000;
|
||||
len = snprintf(buffer, sizeof(buffer), "%s:%d:%d [%d] %f %s:%d NCCL TRACE ",
|
||||
hostname, pid, tid, cudaDev, timestamp, filefunc, line);
|
||||
len += snprintf(buffer+len, sizeof(buffer)-len, "[%d] %f %s:%d NCCL TRACE ", cudaDev, timestamp, filefunc, line);
|
||||
}
|
||||
len = std::min(len, sizeof(buffer)-1); // prevent overflows
|
||||
|
||||
// Add the message as given by the call site.
|
||||
va_list vargs;
|
||||
va_start(vargs, fmt);
|
||||
len += vsnprintf(buffer+len, sizeof(buffer)-len, fmt, vargs);
|
||||
va_end(vargs);
|
||||
// vsnprintf may return len >= sizeof(buffer) in the case of a truncated output.
|
||||
// Rewind len so that we can replace the final \0 by \n
|
||||
if (len >= sizeof(buffer)) len = sizeof(buffer)-1;
|
||||
if (len) {
|
||||
buffer[len++] = '\n';
|
||||
fwrite(buffer, 1, len, ncclDebugFile);
|
||||
}
|
||||
// Rewind len so that we can replace the final \0 by "\n"
|
||||
len = std::min(len, sizeof(buffer)-1); // prevent overflows
|
||||
|
||||
// Add a newline and write it to the debug file. No terminating null is
|
||||
// necessary since we write bytes instead of the string.
|
||||
buffer[len++] = '\n';
|
||||
fwrite(buffer, 1, len, ncclDebugFile);
|
||||
}
|
||||
|
||||
NCCL_API(void, ncclResetDebugInit);
|
||||
|
||||
새 이슈에서 참조
사용자 차단