Commit 85db7de4 authored by lishen's avatar lishen
Browse files

基本实现bootstrap功能,所有rank硬件信息共享

parent a4ac3320
#include <stdint.h>
#include <hip/hip_runtime.h>
#include <hip/hip_runtime_api.h>
#include "base.h"
#include "hardware_utils.h"
#include "bootstrap.h"
namespace sccl {
namespace hardware {
namespace topology {
namespace bootstrap {
// 全局变量,全部节点的信息
struct BootstrapComm bootstrap_comm;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
scclResult_t scclGetUniqueId(scclUniqueId* unique_id) {
auto handle = reinterpret_cast<struct BootstrapHandle*>(unique_id);
NEQCHECK(sizeof(struct BootstrapHandle), SCCL_UNIQUE_ID_BYTES);
SCCLCHECK(bootstrapGetUniqueId(handle));
return scclSuccess;
}
scclResult_t sccl_init(const scclUniqueId* unique_id, int rank, int nRanks) {
// -------------------------- 1.获取0号rank的地址信息 ----------------------------------- //
auto root_handle = reinterpret_cast<const struct BootstrapHandle*>(unique_id);
EQCHECK(root_handle->magic, 0); // 检查handle是否已经更新
// -------------------------- 2.初始化获取所有节点的node信息 ----------------------------------- //
auto sccl_bootstrap = std::make_unique<Bootstrap>(root_handle, rank, nRanks);
SCCLCHECK(sccl_bootstrap->init(&bootstrap_comm));
// // -------------------------- 3.MPI allgather设置unique_id的整合 ----------------------------------- //
// auto unique_ids_chr = reinterpret_cast<const char*>(unique_ids);
// // -------------------------- 3.MPI allgather设置unique_id的整合 ----------------------------------- //
// std::vector<scclUniqueId> unique_id_vec(nRanks);
// MPI_Allgather(&unique_id, sizeof(scclUniqueId), MPI_BYTE, &unique_id_vec[0], sizeof(scclUniqueId), MPI_BYTE, MPI_COMM_WORLD);
// for(int i = 0; i < nRanks; ++i) {
// auto root_handle = reinterpret_cast<const struct BootstrapHandle*>(unique_ids_chr + i * sizeof(struct BootstrapHandle));
// printf("rank=%d, i=%d, unique_ids hosthash=%lu\n", root_handle->rank, i, root_handle->hostHash);
// }
// ByteSpan<struct BootstrapHandle> unique_ids_span(unique_ids_chr, nRanks * sizeof(struct BootstrapHandle));
// // -------------------------- 2.设置基础信息 ----------------------------------- //
// INFO(SCCL_LOG_TOPO, "Bootstrap ...\n");
// struct scclRankInfo rank_info;
// rank_info.rank = rank;
// rank_info.nRanks = nRanks;
// // 在每个进程中设置 root_handle 的值
// root_handle.rank = rank_info->rank;
// root_handle.hostHash = getHostHash();
// scclSocketAddress_t localSocketAddr = sccl_bootstrap->getLocalSocketAddr();
// memcpy(&root_handle.addr, &localSocketAddr, sizeof(scclSocketAddress_t));
// #if 1
// char line[100];
// sprintf(line, "pos 55: rank=%d", rank);
// SCCLCHECK(hardware::net::printSocketAddr(&root_handle.addr, line));
// printf("root_handle.hostHash rank=%d, hash=%lu\n", rank, root_handle.hostHash);
// #endif
// // -------------------------- 3.收集所有进程的 root_handle 信息 ----------------------------------- //
// std::vector<char> recvBuffer(nRanks * sendBuffer.size());
// SCCLCHECK(mpi::wrap_mpi_allgather(sendBuffer.data(), sendBuffer.size(), MPI_BYTE, recvBuffer.data(), sendBuffer.size(), MPI_BYTE, MPI_COMM_WORLD));
// -------------------------- 4.设置各个节点的基础信息 ----------------------------------- //
// SCCLCHECK(sccl_bootstrap->bootstrapInit(rank_info, recvBuffer.data()));
// -------------------------- 5.根据各个节点的基础信息计算topo结果 ----------------------------------- //
return scclSuccess;
}
scclResult_t sccl_finalize() {
// 设置一些全局变量的重置和销毁
// 设置socket等硬件监听的关闭
// void BootstrapComm::destroy() {
if(bootstrap_comm.nRanks > 0) {
bootstrap_comm.destroy();
}
return scclSuccess;
}
} // namespace bootstrap
} // namespace topology
} // namespace hardware
} // namespace sccl
#pragma once
#include <stdint.h>
#include "base.h"
#include "comm.h"
namespace sccl {
namespace hardware {
namespace topology {
namespace bootstrap {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
scclResult_t scclGetUniqueId(scclUniqueId* unique_id);
scclResult_t sccl_init(const scclUniqueId* unique_id, int rank, int nRanks);
scclResult_t sccl_finalize();
} // namespace bootstrap
} // namespace topology
} // namespace hardware
} // namespace sccl
This diff is collapsed.
#pragma once #pragma once
#include <type_traits>
#include <assert.h> #include <assert.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
...@@ -20,8 +21,7 @@ namespace hardware { ...@@ -20,8 +21,7 @@ namespace hardware {
namespace net { namespace net {
namespace ipc_socket { namespace ipc_socket {
#define SCCL_IPC_SOCKNAME_LEN 64 constexpr int SCCL_IPC_SOCKNAME_LEN = 64;
#define SCCL_IPC_SOCKNAME_STR "/tmp/sccl-socket-%d-%lx"
// 定义IPC套接字结构体 // 定义IPC套接字结构体
struct scclIpcSocketHandle { struct scclIpcSocketHandle {
...@@ -40,11 +40,9 @@ struct DataPackage { ...@@ -40,11 +40,9 @@ struct DataPackage {
class scclIpcSocket { class scclIpcSocket {
public: public:
// 构造函数和析构函数 // 构造函数和析构函数
scclIpcSocket(int localRank, int localRanks, uint64_t hash, volatile uint32_t* abortFlag = nullptr); scclIpcSocket(int localRank, int nlocalRanks, uint64_t hash, volatile uint32_t* abortFlag = nullptr);
virtual ~scclIpcSocket(); virtual ~scclIpcSocket();
// 初始化IPC套接字
scclResult_t scclIpcSocketInit(volatile uint32_t* abortFlag);
// 设置 abortFlag 的函数 // 设置 abortFlag 的函数
scclResult_t setAbortFlag(volatile uint32_t* flag); scclResult_t setAbortFlag(volatile uint32_t* flag);
// 获取 abortFlag 的函数 // 获取 abortFlag 的函数
...@@ -58,27 +56,32 @@ public: ...@@ -58,27 +56,32 @@ public:
/* /*
并行计算时,不同的进程可能需要访问相同的文件或网络资源。通过发送文件描述符,可以避免多个进程重复打开相同的文件或建立相同的网络连接,从而节省资源和时间。 并行计算时,不同的进程可能需要访问相同的文件或网络资源。通过发送文件描述符,可以避免多个进程重复打开相同的文件或建立相同的网络连接,从而节省资源和时间。
*/ */
// 发送文件描述符 // 发送/接收文件描述符
scclResult_t scclIpcSocketSendFd(const int sendFd, int dst_rank); scclResult_t scclIpcSocketSendFd(const int sendFd, int dst_rank);
// 接收文件描述符
scclResult_t scclIpcSocketRecvFd(int* fd); scclResult_t scclIpcSocketRecvFd(int* fd);
// 通过Unix域套接字发送数据到指定目标,阻塞方式 // 通过Unix域套接字发送/接收数据到指定目标
scclResult_t scclIpcSocketSendData(const void* data, size_t dataLen, int dst_rank); scclResult_t scclIpcSocketSendData(const void* data, size_t dataLen, int dst_rank);
// 通过Unix域套接字接收数据,阻塞方式
scclResult_t scclIpcSocketRecvData(void* buffer, size_t bufferLen, size_t* receivedLen); scclResult_t scclIpcSocketRecvData(void* buffer, size_t bufferLen, size_t* receivedLen);
// 通过Unix域套接字发送数据到指定目标,非阻塞方式
scclResult_t scclIpcSocketSendDataNonBlocking(const void* data, size_t dataLen, int dst_rank);
// 通过Unix域套接字接收数据,非阻塞方式
scclResult_t scclIpcSocketRecvDataNonBlocking(void* buffer, size_t bufferLen, size_t* receivedLen);
// 通过Unix域套接字发送/接收数据到指定目标,并发送ack确保发送成功
scclResult_t scclIpcSocketSendDataWithAck(const void* data, size_t dataLen, int dst_rank);
scclResult_t scclIpcSocketRecvDataAndSendAck(void* buffer, size_t bufferLen, size_t* receivedLen, int src_rank);
//////////////////////////////////////////////////////////////////////////////////////////////////////
// local rank内的allgather操作。保证接收顺序 // local rank内的allgather操作。保证接收顺序
scclResult_t scclIpcSocketAllgather(const void* sendData, void* recvData, size_t dataLen, bool wait = true); scclResult_t scclIpcSocketAllgather(const void* sendData, void* recvData, size_t dataLen, bool wait = true);
// local rank内的allgather操作。为了性能,不保证接收顺序,所以发送的信息中需要添加进程ID // local rank内的allgather操作。为了性能,不保证接收顺序,所以发送的信息中需要添加进程ID
scclResult_t scclIpcSocketAllgatherSync(const void* sendData, void* recvData, size_t dataLen, bool wait = true); scclResult_t scclIpcSocketAllgatherSync(const void* sendData, void* recvData, size_t dataLen, bool wait = true);
// local rank内的broadcast操作 // local rank内的broadcast操作
scclResult_t scclIpcSocketBroadcast(const void* sendData, void* recvData, size_t dataLen, int root, bool wait = true); scclResult_t scclIpcSocketBroadcast(void* data, size_t dataLen, int root, bool wait = true);
private:
// 初始化IPC套接字
scclResult_t scclIpcSocketInit(volatile uint32_t* abortFlag);
scclResult_t getScclIpcSocknameStr(int rank, uint64_t hash, char* out_str, int* out_len);
private: private:
// 定义并初始化一个 scclIpcSocket 结构体,用于处理 IPC 套接字连接 // 定义并初始化一个 scclIpcSocket 结构体,用于处理 IPC 套接字连接
...@@ -92,13 +95,14 @@ private: ...@@ -92,13 +95,14 @@ private:
const volatile uint32_t* my_abortFlag; const volatile uint32_t* my_abortFlag;
// 进程id信息 // 进程id信息
int localRank = -1; int localRank = -1;
int localRanks = 0; int nlocalRanks = 0;
// 线程池指针 // 线程池指针
ThreadPool* pthread_pool = nullptr; ThreadPool* pthread_pool = nullptr;
// 设置超时时间为 10000 毫秒 // 设置超时时间为无限长
int timeoutMs = 10000; int timeoutMs = -1;
static constexpr int ACK_SIZE = 8;
}; };
} // namespace ipc_socket } // namespace ipc_socket
......
...@@ -17,8 +17,7 @@ namespace net { ...@@ -17,8 +17,7 @@ namespace net {
scclResult_t printSocketAddr(union net_socket::scclSocketAddress* sock_addr, const char* prefix) { scclResult_t printSocketAddr(union net_socket::scclSocketAddress* sock_addr, const char* prefix) {
char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2]; char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2];
net::net_socket::scclSocketToString(sock_addr, line); net::net_socket::scclSocketToString(sock_addr, line);
printf("==========================================\n%s addr: %s"
printf("\n==========================================\n%s addr: %s"
"\n==========================================\n", "\n==========================================\n",
prefix, prefix,
line); line);
......
...@@ -54,8 +54,7 @@ SCCL_PARAM(IbGdrFlushDisable, "GDR_FLUSH_DISABLE", 0); ...@@ -54,8 +54,7 @@ SCCL_PARAM(IbGdrFlushDisable, "GDR_FLUSH_DISABLE", 0);
SCCL_PARAM(IbSplitDataOnQps, "IB_SPLIT_DATA_ON_QPS", 1); SCCL_PARAM(IbSplitDataOnQps, "IB_SPLIT_DATA_ON_QPS", 1);
///////////////////////////////////////// 参数及结构体设置 ///////////////////////////////////////// ///////////////////////////////////////// 参数及结构体设置 /////////////////////////////////////////
#define MAXNAMESIZE 64 constexpr int MAXNAMESIZE = 64;
#define MAX_IF_NAME_SIZE 16
static char scclIbIfName[MAX_IF_NAME_SIZE + 1]; // 用于存储网络接口名称的字符数组 static char scclIbIfName[MAX_IF_NAME_SIZE + 1]; // 用于存储网络接口名称的字符数组
static union net_socket::scclSocketAddress scclIbIfAddr; // 定义一个联合体类型的变量,用于存储网络接口地址 static union net_socket::scclSocketAddress scclIbIfAddr; // 定义一个联合体类型的变量,用于存储网络接口地址
...@@ -660,10 +659,12 @@ scclResult_t scclNetIb::scclIbRtsQp(struct ibv_qp* qp) { ...@@ -660,10 +659,12 @@ scclResult_t scclNetIb::scclIbRtsQp(struct ibv_qp* qp) {
return scclSuccess; return scclSuccess;
} }
#define SCCL_NET_IB_REQ_UNUSED 0 typedef enum : int {
#define SCCL_NET_IB_REQ_SEND 1 SCCL_NET_IB_REQ_UNUSED = 0,
#define SCCL_NET_IB_REQ_RECV 2 SCCL_NET_IB_REQ_SEND = 1,
#define SCCL_NET_IB_REQ_FLUSH 3 SCCL_NET_IB_REQ_RECV = 2,
SCCL_NET_IB_REQ_FLUSH = 3
} NetIbReq_t;
const char* reqTypeStr[] = {"Unused", "Send", "Recv", "Flush"}; const char* reqTypeStr[] = {"Unused", "Send", "Recv", "Flush"};
// The SendFifo needs to be 32-byte aligned and each element needs // The SendFifo needs to be 32-byte aligned and each element needs
......
...@@ -11,7 +11,7 @@ namespace hardware { ...@@ -11,7 +11,7 @@ namespace hardware {
namespace net { namespace net {
namespace net_socket { namespace net_socket {
#define MAX_LINE_LEN (2047) constexpr int MAX_LINE_LEN = (2047);
/* Init functions */ /* Init functions */
static int scclNetIfs = -1; static int scclNetIfs = -1;
......
...@@ -27,7 +27,7 @@ namespace socket_base { ...@@ -27,7 +27,7 @@ namespace socket_base {
*/ */
static int scclGetSocketFamily(void) { static int scclGetSocketFamily(void) {
int family = -1; // Family selection is not forced, will use first one found int family = -1; // Family selection is not forced, will use first one found
const char* env = scclGetEnv("SCCL_SOCKET_FAMILY"); const char* env = getenv("SCCL_SOCKET_FAMILY");
if(env == NULL) if(env == NULL)
return family; return family;
...@@ -240,6 +240,17 @@ static scclResult_t socketWait(int op, struct scclSocket* sock, void* ptr, int s ...@@ -240,6 +240,17 @@ static scclResult_t socketWait(int op, struct scclSocket* sock, void* ptr, int s
return scclSuccess; return scclSuccess;
} }
/**
* 尝试接受一个socket连接
*
* @param sock 指向scclSocket结构体的指针,包含待接受的socket信息
* @return scclResult_t 返回操作结果:
* - scclSuccess 表示成功接受连接
* - scclSystemError 表示接受失败且错误不是EAGAIN或EWOULDBLOCK
*
* @note 如果accept失败且errno为EAGAIN或EWOULDBLOCK,函数会返回scclSuccess,
* 表示这是非阻塞模式下正常情况,可以稍后重试
*/
static scclResult_t socketTryAccept(struct scclSocket* sock) { static scclResult_t socketTryAccept(struct scclSocket* sock) {
socklen_t socklen = sizeof(union scclSocketAddress); socklen_t socklen = sizeof(union scclSocketAddress);
sock->fd = accept(sock->acceptFd, &sock->addr.sa, &socklen); sock->fd = accept(sock->acceptFd, &sock->addr.sa, &socklen);
...@@ -252,6 +263,18 @@ static scclResult_t socketTryAccept(struct scclSocket* sock) { ...@@ -252,6 +263,18 @@ static scclResult_t socketTryAccept(struct scclSocket* sock) {
return scclSuccess; return scclSuccess;
} }
/**
* 完成socket连接的最终接受过程
*
* 1. 设置TCP_NODELAY选项
* 2. 接收并验证magic值
* 3. 接收并验证socket类型
* 4. 根据验证结果设置socket状态
*
* @param sock 要完成接受的socket指针
* @return scclSuccess 成功完成接受或忽略无效连接
* @return scclInternalError 类型验证失败
*/
static scclResult_t socketFinalizeAccept(struct scclSocket* sock) { static scclResult_t socketFinalizeAccept(struct scclSocket* sock) {
uint64_t magic; uint64_t magic;
enum scclSocketType type; enum scclSocketType type;
...@@ -286,6 +309,19 @@ static scclResult_t socketFinalizeAccept(struct scclSocket* sock) { ...@@ -286,6 +309,19 @@ static scclResult_t socketFinalizeAccept(struct scclSocket* sock) {
return scclSuccess; return scclSuccess;
} }
/**
* 启动socket连接过程
*
* 根据asyncFlag决定使用阻塞或非阻塞connect()操作。处理连接过程中的各种状态:
* - 连接成功:返回scclSuccess,状态设为scclSocketStateConnected
* - EINPROGRESS:返回scclSuccess,状态设为scclSocketStateConnectPolling
* - ECONNREFUSED:进行重试,超过RETRY_REFUSED_TIMES次后返回scclRemoteError
* - ETIMEDOUT:进行重试,超过RETRY_TIMEDOUT_TIMES次后返回scclRemoteError
* - 其他错误:返回scclSystemError
*
* @param sock 要连接的socket结构体指针
* @return scclResult_t 连接结果状态码
*/
static scclResult_t socketStartConnect(struct scclSocket* sock) { static scclResult_t socketStartConnect(struct scclSocket* sock) {
/* blocking/non-blocking connect() is determined by asyncFlag. */ /* blocking/non-blocking connect() is determined by asyncFlag. */
int ret = connect(sock->fd, &sock->addr.sa, sock->salen); int ret = connect(sock->fd, &sock->addr.sa, sock->salen);
...@@ -1024,16 +1060,19 @@ scclResult_t scclSocketReady(struct scclSocket* sock, int* running) { ...@@ -1024,16 +1060,19 @@ scclResult_t scclSocketReady(struct scclSocket* sock, int* running) {
/** /**
* @brief 接受一个socket连接 * @brief 接受一个socket连接
* *
* @param sock 用于接收连接的socket对象 * 该函数用于从监听socket接受一个新的连接,并将新连接的socket信息填充到指定的socket结构中。
* @param listenSock 监听状态的socket对象 *
* @return scclResult_t 返回操作结果状态码: * @param sock 用于存储新连接socket信息的结构体指针
* @param listenSock 监听socket的结构体指针
*
* @return scclResult_t 返回操作结果:
* - scclSuccess: 操作成功 * - scclSuccess: 操作成功
* - scclInvalidArgument: 参数无效 * - scclInvalidArgument: 参数无效
* - scclSystemError: 系统错误 * - scclSystemError: 系统错误
* - scclInternalError: 内部错误 * - scclInternalError: 内部错误
* *
* @note 该函数会阻塞直到连接被接受或发生错误。如果设置了异步标志(asyncFlag), * @note 该函数会阻塞直到连接被接受或发生错误,除非设置了异步标志
* 则会在后台处理连接请求。可以通过abortFlag来中操作。 * 如果设置了abortFlag,可以通过设置该标志来中操作。
*/ */
scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* listenSock) { scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* listenSock) {
scclResult_t ret = scclSuccess; scclResult_t ret = scclSuccess;
......
...@@ -11,15 +11,16 @@ ...@@ -11,15 +11,16 @@
namespace sccl { namespace sccl {
namespace hardware { namespace hardware {
namespace net { namespace net {
namespace net_socket {
#define MAX_IFS 16 // 最大接口数量 #define SLEEP_INT 1000 // 连接重试的休眠间隔,单位为微秒
#define MAX_IF_NAME_SIZE 16 // 每个接口名称的最大长度 constexpr int MAX_IFS = 16; // 最大接口数量
#define SLEEP_INT 1000 // 连接重试的休眠间隔,单位为微秒 constexpr int MAX_IF_NAME_SIZE = 16; // 每个接口名称的最大长度
#define RETRY_REFUSED_TIMES 2e4 // 在报告超时之前,连接被拒绝的重试次数(总计20秒) constexpr int RETRY_REFUSED_TIMES = 2e4; // 在报告超时之前,连接被拒绝的重试次数(总计20秒)
#define RETRY_TIMEDOUT_TIMES 3 // 连接超时的重试次数(每次重试可能需要20秒) constexpr int RETRY_TIMEDOUT_TIMES = 3; // 连接超时的重试次数(每次重试可能需要20秒)
#define SOCKET_NAME_MAXLEN (NI_MAXHOST + NI_MAXSERV) // 套接字名称的最大长度,包括主机名和服务名 constexpr int SOCKET_NAME_MAXLEN = (NI_MAXHOST + NI_MAXSERV); // 套接字名称的最大长度,包括主机名和服务名
#define SCCL_SOCKET_MAGIC 0x564ab9f2fc4b9d6cULL // 用于标识套接字的魔数 constexpr uint64_t SCCL_SOCKET_MAGIC = 0x564ab9f2fc4b9d6cULL; // 用于标识套接字的魔数
namespace net_socket {
/* 用于存储IPv4/IPv6通用套接字地址的联合体 */ /* 用于存储IPv4/IPv6通用套接字地址的联合体 */
union scclSocketAddress { // 联合体用于存储不同类型的套接字地址 union scclSocketAddress { // 联合体用于存储不同类型的套接字地址
...@@ -64,9 +65,6 @@ struct scclSocket { ...@@ -64,9 +65,6 @@ struct scclSocket {
enum scclSocketType type; // 套接字类型 enum scclSocketType type; // 套接字类型
}; };
#define SCCL_SOCKET_SEND 0
#define SCCL_SOCKET_RECV 1
//////////////////////////////////// socket工具 //////////////////////////////////// //////////////////////////////////// socket工具 ////////////////////////////////////
// 将地址转换为字符串 // 将地址转换为字符串
const char* scclSocketToString(const union scclSocketAddress* addr, char* buf, const int numericHostForm = 1); const char* scclSocketToString(const union scclSocketAddress* addr, char* buf, const int numericHostForm = 1);
...@@ -99,21 +97,148 @@ scclResult_t scclSocketInit(struct scclSocket* sock, ...@@ -99,21 +97,148 @@ scclResult_t scclSocketInit(struct scclSocket* sock,
enum scclSocketType type = scclSocketTypeUnknown, enum scclSocketType type = scclSocketTypeUnknown,
volatile uint32_t* abortFlag = NULL, volatile uint32_t* abortFlag = NULL,
int asyncFlag = 0); int asyncFlag = 0);
// 创建一个监听socket。sock->addr可以预先填充IP和端口信息。成功调用后设置sock->fd
// 创建一个监听socket,用于服务器端等待客户端的连接请求。
// 是否阻塞: 阻塞,直到有客户端连接。
// 使用端: 服务器端。
// 使用时机: 当服务器准备好接受客户端连接时使用。
scclResult_t scclSocketListen(struct scclSocket* sock); scclResult_t scclSocketListen(struct scclSocket* sock);
// 获取socket地址
// 获取socket的地址信息。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 需要获取当前socket绑定的地址和端口信息时使用。
scclResult_t scclSocketGetAddr(struct scclSocket* sock, union scclSocketAddress* addr); scclResult_t scclSocketGetAddr(struct scclSocket* sock, union scclSocketAddress* addr);
// 连接到sock->addr。成功调用后设置sock->fd。
// 客户端主动连接到服务器端的套接字。
// 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。
// 使用端: 客户端。
// 使用时机: 客户端需要与服务器建立连接时使用。
scclResult_t scclSocketConnect(struct scclSocket* sock, int portReuse = 0); scclResult_t scclSocketConnect(struct scclSocket* sock, int portReuse = 0);
// 返回socket连接状态。
// 检查socket连接状态,判断是否已准备好进行通信。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 在进行数据传输之前,检查连接是否已建立。
scclResult_t scclSocketReady(struct scclSocket* sock, int* running); scclResult_t scclSocketReady(struct scclSocket* sock, int* running);
// 接受来自listenSock->fd的传入连接,并在sock->fd中保持文件描述符,远程端IP/端口在sock->addr中。
scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* ulistenSock); // 服务器端接受客户端的连接请求,创建一个新的套接字用于通信。
// 获取socket文件描述符 // 是否阻塞: 可以选择阻塞或非阻塞,取决于实现和调用方式。
// 使用端: 服务器端。
// 使用时机: 服务器端在监听socket上收到客户端连接请求后使用。
scclResult_t scclSocketAccept(struct scclSocket* sock, struct scclSocket* listenSock);
// 获取socket的文件描述符。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 需要直接操作socket的文件描述符时使用,例如在多路复用中。
scclResult_t scclSocketGetFd(struct scclSocket* sock, int* fd); scclResult_t scclSocketGetFd(struct scclSocket* sock, int* fd);
// 设置socket文件描述符
// 设置socket的文件描述符。
// 是否阻塞: 不阻塞。
// 使用端: 通用(客户端和服务器端均可使用)。
// 使用时机: 在已有文件描述符的基础上创建socket对象时使用。
scclResult_t scclSocketSetFd(int fd, struct scclSocket* sock); scclResult_t scclSocketSetFd(int fd, struct scclSocket* sock);
//////////////////////////////////////////////////////////////////////////////////////////////////
/**
* @brief 管理套接字的基类,提供套接字的获取、释放和初始化功能。
*
* 该类包含一个指向scclSocket结构体的指针sock_和一个布尔值owned_,
* 用于跟踪套接字的所有权。提供了获取和释放套接字的方法,
* 以及一个初始化方法来设置套接字的各种参数。
*/
class scclSocketManager {
public:
struct scclSocket* getSocket() { return sock_; }
struct scclSocket* releaseSocket() {
owned_ = false;
return sock_;
}
protected:
struct scclSocket* sock_;
bool owned_;
scclSocketManager() : sock_(nullptr), owned_(true) {}
virtual ~scclSocketManager() {
if(owned_ && sock_) {
scclSocketClose(sock_);
delete sock_;
}
}
// 默认的初始化过程
scclResult_t init(union scclSocketAddress* addr = NULL,
uint64_t magic = SCCL_SOCKET_MAGIC,
enum scclSocketType type = scclSocketTypeUnknown,
volatile uint32_t* abortFlag = NULL,
int asyncFlag = 0) {
sock_ = new struct scclSocket;
return scclSocketInit(sock_, addr, magic, type, abortFlag, asyncFlag);
}
private:
// 禁止拷贝构造和赋值操作
scclSocketManager(const scclSocketManager&);
scclSocketManager& operator=(const scclSocketManager&);
};
/**
* @brief 管理服务器端套接字的类,继承自scclSocketManager。
*
* 该类在构造时初始化套接字,并将其设置为监听模式。
*/
class scclSocketServerManager : public scclSocketManager {
public:
scclSocketServerManager(union scclSocketAddress* addr,
uint64_t magic = SCCL_SOCKET_MAGIC,
enum scclSocketType type = scclSocketTypeUnknown,
volatile uint32_t* abortFlag = NULL,
int asyncFlag = 0) {
SCCLCHECK(init(addr, magic, type, abortFlag, asyncFlag));
SCCLCHECK(scclSocketListen(sock_));
SCCLCHECK(scclSocketGetAddr(sock_, addr));
#if 0
char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2];
net::net_socket::scclSocketToString(addr, line);
printf("server listening addr: %s\n", line);
#endif
}
};
/**
* @brief 管理客户端套接字的类,继承自scclSocketManager。
*
* 该类在构造时初始化套接字,并尝试连接到服务器。
*/
class scclSocketClientManager : public scclSocketManager {
public:
scclSocketClientManager(union scclSocketAddress* addr,
uint64_t magic = SCCL_SOCKET_MAGIC,
enum scclSocketType type = scclSocketTypeUnknown,
volatile uint32_t* abortFlag = NULL,
int asyncFlag = 0) {
SCCLCHECK(init(addr, magic, type, abortFlag, asyncFlag));
SCCLCHECK(scclSocketConnect(sock_));
#if 0
char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2];
net::net_socket::scclSocketToString(addr, line);
printf("client connect addr: %s\n", line);
#endif
}
};
/**
* @brief 管理接受连接的套接字的类,继承自scclSocketManager。
*
* 该类在构造时初始化套接字,并接受来自监听套接字的连接。
*/
class scclSocketAcceptManager : public scclSocketManager {
public:
scclSocketAcceptManager(struct scclSocket* listenSock, int asyncFlag = 0) : scclSocketManager() {
init(NULL, SCCL_SOCKET_MAGIC, scclSocketTypeUnknown, NULL, /*asyncFlag=*/asyncFlag);
scclSocketAccept(sock_, listenSock);
}
};
} // namespace net_socket } // namespace net_socket
} // namespace net } // namespace net
} // namespace hardware } // namespace hardware
......
...@@ -13,6 +13,10 @@ typedef enum { ...@@ -13,6 +13,10 @@ typedef enum {
SCCL_PTR_DMABUF = 0x4 SCCL_PTR_DMABUF = 0x4
} sccl_ptr_t; } sccl_ptr_t;
constexpr int SCCL_SOCKET_SEND = 0;
constexpr int SCCL_SOCKET_RECV = 1;
constexpr int SCCL_NET_HANDLE_MAXSIZE = 128;
////////////////////////////////// 用于定义网络设备 ////////////////////////////////// ////////////////////////////////// 用于定义网络设备 //////////////////////////////////
typedef struct { typedef struct {
char* name; // 主要用于日志记录。 char* name; // 主要用于日志记录。
...@@ -92,8 +96,6 @@ public: ...@@ -92,8 +96,6 @@ public:
////////////////////////////////// 功能函数 ////////////////////////////////// ////////////////////////////////// 功能函数 //////////////////////////////////
// 初始化 ROCm 库 // 初始化 ROCm 库
scclResult_t rocmLibraryInit(void); scclResult_t rocmLibraryInit(void);
#define SCCL_NET_HANDLE_MAXSIZE 128
struct netIf { // 网络接口结构体 struct netIf { // 网络接口结构体
char prefix[64]; // 网络前缀 char prefix[64]; // 网络前缀
int port; // 端口号 int port; // 端口号
......
...@@ -6,45 +6,60 @@ ...@@ -6,45 +6,60 @@
#include "bootstrap_utils.h" #include "bootstrap_utils.h"
#include "bootstrap_net.h" #include "bootstrap_net.h"
#include "thread_pool.h" #include "thread_pool.h"
#include "ipc_socket.h"
namespace sccl { namespace sccl {
namespace hardware { namespace hardware {
namespace topology { namespace topology {
namespace bootstrap { namespace bootstrap {
class scclBootstrap { typedef class sccl::hardware::net::ipc_socket::scclIpcSocket scclIpcSocket_t;
///////////////////////////////////// 用于初始化时的功能函数 //////////////////////////////////////////
scclResult_t bootstrapGetUniqueId(struct BootstrapHandle* handle);
scclResult_t bootstrapCreateRoot(struct BootstrapHandle* handle);
///////////////////////////////////// 用于初始化时的类 //////////////////////////////////////////
class Bootstrap {
public: public:
scclBootstrap(struct scclRankInfo* rank_info, struct scclBootstrapComm* comm); Bootstrap(const struct BootstrapHandle*, int rank, int nRanks);
~scclBootstrap(); ~Bootstrap();
// 初始化bootstrap通信环境 // 初始化bootstrap通信环境
scclResult_t bootstrapInit(const struct scclRankInfo* rank_info, struct scclBootstrapComm* comm); scclResult_t init(struct BootstrapComm* bootstrap_comm);
// 检查bootstrap是否已成功初始化
scclResult_t bootstrapInitCheck();
// 广播节点信息 // 广播节点信息
scclResult_t bootstrapAllGather(struct scclUniqueInfo* unique_info); scclResult_t bootstrapAllGather(struct scclNodeInfo*);
private: private:
// 执行基本的引导程序初始化 // 创建根节点的数据广播
scclResult_t bootstrapBasicInit(); scclResult_t bootstrapRootGatherAndBroadcast(void* send_data, void* recv_data);
// 初始化唯一ID信息结构体 // 初始化唯一ID信息结构体
scclResult_t bootstrapUniqueInfoInit(const struct scclRankInfo* rank_info, scclNet_t* scclNet, struct scclUniqueInfo* unique_info); scclResult_t bootstrapCommInitNodeInfo(scclNet_t* scclNet, struct scclNodeInfo* node_info);
// scclResult_t bootstrapGetAllNodes(const struct scclUniqueInfo* unique_info, struct scclBootstrapComm* comm); // scclResult_t bootstrapGetAllNodes(const struct scclNodeInfo* , struct BootstrapComm* comm);
private: private:
pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER; int rank, nRanks; // 初始化阶段获取MPI的值
bool initialized = false; int localRank, nLocalRanks; // 通过bootstrapRootGatherAndBroadcast函数确定值
bool hsaFineGrainFlag = true; volatile uint32_t* abortFlag; // 中止标志,非阻塞套接字设置
// 外部传入的0号节点的基础信息
const struct BootstrapHandle* root_handle;
// 分配并初始化引导句柄 // 初始化标志
struct scclBootstrapHandle* handle = nullptr; bool socketInitDone;
// 分配并初始化网络结构体 // 互斥锁,用于保护初始化过程的线程安全
class bootstrapNet* bootstrap_net = nullptr; pthread_mutex_t bootstrapMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t bootstrapCond = PTHREAD_COND_INITIALIZER;
int max_pthreads = 0; // 标志是否已经初始化
class ThreadPool* pthread_pool = nullptr; // 线程池变量
int max_pthreads = 0; // 用于存储最大并行线程数的整型变量
class ThreadPool* pthread_pool = nullptr; // 指向ThreadPool类实例的指针,初始值为nullptr
scclIpcSocket_t* ipcsocket = nullptr; // 指向scclIpcSocket类实例的指针,初始值为nullptr
scclSocket_t* my_listen_sock = nullptr; // 指向scclSocket类实例的指针,初始值为nullptr
}; };
} // namespace bootstrap } // namespace bootstrap
......
...@@ -15,18 +15,19 @@ namespace sccl { ...@@ -15,18 +15,19 @@ namespace sccl {
namespace hardware { namespace hardware {
namespace topology { namespace topology {
namespace bootstrap { namespace bootstrap {
namespace bootstrapNet {
bootstrapNet::bootstrapNet(struct scclBootstrapComm* bootstrap_comm) { /* Init functions */
auto unique_info = bootstrap_comm->unique_info; // 用于存储网络接口名称的静态字符数组
// 设置节点内socket通信工具 static char bootstrapNetIfName[net::MAX_IF_NAME_SIZE + 1];
ipcsocket = new scclIpcSocket_t(unique_info->localRank, unique_info->nRanks, unique_info->hostHash, bootstrap_comm->abortFlag); // 用于存储网络接口地址的静态结构体
} static scclSocketAddress_t bootstrapNetIfAddr;
bootstrapNet::~bootstrapNet() { // 静态整型变量,用于指示网络初始化是否已完成(0表示未完成,非0表示已完成)
if(ipcsocket) { static int bootstrapNetInitDone = 0;
delete ipcsocket;
} // 互斥锁,用于保护对上述静态变量的访问,确保线程安全
} static pthread_mutex_t bootstrapNetLock = PTHREAD_MUTEX_INITIALIZER;
/** /**
* @brief 初始化引导网络 * @brief 初始化引导网络
...@@ -43,7 +44,7 @@ bootstrapNet::~bootstrapNet() { ...@@ -43,7 +44,7 @@ bootstrapNet::~bootstrapNet() {
* - scclSystemError: 找不到匹配的网络接口 * - scclSystemError: 找不到匹配的网络接口
* - scclInternalError: 找不到可用的网络接口 * - scclInternalError: 找不到可用的网络接口
*/ */
scclResult_t bootstrapNet::bootstrapNetInit() { scclResult_t bootstrapNetInit() {
if(bootstrapNetInitDone == 0) { if(bootstrapNetInitDone == 0) {
pthread_mutex_lock(&bootstrapNetLock); pthread_mutex_lock(&bootstrapNetLock);
if(bootstrapNetInitDone == 0) { if(bootstrapNetInitDone == 0) {
...@@ -54,18 +55,18 @@ scclResult_t bootstrapNet::bootstrapNetInit() { ...@@ -54,18 +55,18 @@ scclResult_t bootstrapNet::bootstrapNetInit() {
WARN("Invalid SCCL_COMM_ID, please use format: <ipv4>:<port> or [<ipv6>]:<port> or <hostname>:<port>"); WARN("Invalid SCCL_COMM_ID, please use format: <ipv4>:<port> or [<ipv6>]:<port> or <hostname>:<port>");
return scclInvalidArgument; return scclInvalidArgument;
} }
if(net::net_socket::scclFindInterfaceMatchSubnet(bootstrapNetIfName, &bootstrapNetIfAddr, &remoteAddr, MAX_IF_NAME_SIZE, 1) <= 0) { if(net::net_socket::scclFindInterfaceMatchSubnet(bootstrapNetIfName, &bootstrapNetIfAddr, &remoteAddr, net::MAX_IF_NAME_SIZE, 1) <= 0) {
WARN("NET/Socket : No usable listening interface found"); WARN("NET/Socket : No usable listening interface found");
return scclSystemError; return scclSystemError;
} }
} else { } else {
int nIfs = net::net_socket::scclFindSocketInterfaces(bootstrapNetIfName, &bootstrapNetIfAddr, MAX_IF_NAME_SIZE, 1); int nIfs = net::net_socket::scclFindSocketInterfaces(bootstrapNetIfName, &bootstrapNetIfAddr, net::MAX_IF_NAME_SIZE, 1);
if(nIfs <= 0) { if(nIfs <= 0) {
WARN("Bootstrap : no socket interface found"); WARN("Bootstrap : no socket interface found");
return scclInternalError; return scclInternalError;
} }
} }
char line[SOCKET_NAME_MAXLEN + MAX_IF_NAME_SIZE + 2]; char line[net::SOCKET_NAME_MAXLEN + net::MAX_IF_NAME_SIZE + 2];
sprintf(line, "%s:", bootstrapNetIfName); sprintf(line, "%s:", bootstrapNetIfName);
net::net_socket::scclSocketToString(&bootstrapNetIfAddr, line + strlen(line)); net::net_socket::scclSocketToString(&bootstrapNetIfAddr, line + strlen(line));
INFO(SCCL_LOG_BOOTSTRAP, "Bootstrap : Using %s", line); INFO(SCCL_LOG_BOOTSTRAP, "Bootstrap : Using %s", line);
...@@ -76,6 +77,8 @@ scclResult_t bootstrapNet::bootstrapNetInit() { ...@@ -76,6 +77,8 @@ scclResult_t bootstrapNet::bootstrapNetInit() {
return scclSuccess; return scclSuccess;
} }
scclSocketAddress_t getLocalSocketAddr() { return bootstrapNetIfAddr; }
// Additional sync functions // Additional sync functions
/** /**
* 通过网络发送数据 * 通过网络发送数据
...@@ -87,7 +90,7 @@ scclResult_t bootstrapNet::bootstrapNetInit() { ...@@ -87,7 +90,7 @@ scclResult_t bootstrapNet::bootstrapNetInit() {
* *
* @note 先发送数据大小(sizeof(int)),再发送实际数据 * @note 先发送数据大小(sizeof(int)),再发送实际数据
*/ */
scclResult_t bootstrapNet::bootstrapNetSend(scclSocket_t* sock, void* data, int size) { scclResult_t bootstrapNetSend(scclSocket_t* sock, void* data, int size) {
SCCLCHECK(net::net_socket::scclSocketSend(sock, &size, sizeof(int))); SCCLCHECK(net::net_socket::scclSocketSend(sock, &size, sizeof(int)));
SCCLCHECK(net::net_socket::scclSocketSend(sock, data, size)); SCCLCHECK(net::net_socket::scclSocketSend(sock, data, size));
return scclSuccess; return scclSuccess;
...@@ -103,7 +106,7 @@ scclResult_t bootstrapNet::bootstrapNetSend(scclSocket_t* sock, void* data, int ...@@ -103,7 +106,7 @@ scclResult_t bootstrapNet::bootstrapNetSend(scclSocket_t* sock, void* data, int
* *
* @note 如果接收到的数据大小超过缓冲区大小,会截断数据并返回scclInternalError * @note 如果接收到的数据大小超过缓冲区大小,会截断数据并返回scclInternalError
*/ */
scclResult_t bootstrapNet::bootstrapNetRecv(scclSocket_t* sock, void* data, int size) { scclResult_t bootstrapNetRecv(scclSocket_t* sock, void* data, int size) {
int recvSize; int recvSize;
SCCLCHECK(net::net_socket::scclSocketRecv(sock, &recvSize, sizeof(int))); SCCLCHECK(net::net_socket::scclSocketRecv(sock, &recvSize, sizeof(int)));
if(recvSize > size) { if(recvSize > size) {
...@@ -114,6 +117,7 @@ scclResult_t bootstrapNet::bootstrapNetRecv(scclSocket_t* sock, void* data, int ...@@ -114,6 +117,7 @@ scclResult_t bootstrapNet::bootstrapNetRecv(scclSocket_t* sock, void* data, int
return scclSuccess; return scclSuccess;
} }
} // namespace bootstrapNet
} // namespace bootstrap } // namespace bootstrap
} // namespace topology } // namespace topology
} // namespace hardware } // namespace hardware
......
...@@ -4,41 +4,24 @@ ...@@ -4,41 +4,24 @@
#include "base.h" #include "base.h"
#include "socket.h" #include "socket.h"
#include "bootstrap_utils.h" #include "bootstrap_utils.h"
#include "ipc_socket.h"
namespace sccl { namespace sccl {
namespace hardware { namespace hardware {
namespace topology { namespace topology {
namespace bootstrap { namespace bootstrap {
namespace bootstrapNet {
typedef class sccl::hardware::net::ipc_socket::scclIpcSocket scclIpcSocket_t; // 初始化
scclResult_t bootstrapNetInit();
class bootstrapNet { scclSocketAddress_t getLocalSocketAddr();
public:
// 构造函数
bootstrapNet(struct scclBootstrapComm* bootstrap_comm);
virtual ~bootstrapNet();
// 初始化 // 通过socket发送数据
scclResult_t bootstrapNetInit(); scclResult_t bootstrapNetSend(scclSocket_t* sock, void* data, int size);
// 通过socket发送数据 // 通过socket接收数据
scclResult_t bootstrapNetSend(scclSocket_t* sock, void* data, int size); scclResult_t bootstrapNetRecv(scclSocket_t* sock, void* data, int size);
// 通过socket接收数据
scclResult_t bootstrapNetRecv(scclSocket_t* sock, void* data, int size);
public:
/* Init functions */
char bootstrapNetIfName[MAX_IF_NAME_SIZE + 1];
scclSocketAddress_t bootstrapNetIfAddr;
private:
int bootstrapNetInitDone = 0;
pthread_mutex_t bootstrapNetLock = PTHREAD_MUTEX_INITIALIZER;
// 用于节点内socket通信
scclIpcSocket_t* ipcsocket = nullptr;
};
} // namespace bootstrapNet
} // namespace bootstrap } // namespace bootstrap
} // namespace topology } // namespace topology
} // namespace hardware } // namespace hardware
......
...@@ -7,6 +7,32 @@ namespace hardware { ...@@ -7,6 +7,32 @@ namespace hardware {
namespace topology { namespace topology {
namespace bootstrap { namespace bootstrap {
////////////////////////////// 结构体定义 //////////////////////////////
// 构造函数定义
scclNodeInfoSet::scclNodeInfoSet(int nRanks) : nUniqueInfos(nRanks) {
printf("scclNodeInfoSet 构造函数\n");
node_info_vec.reserve(nRanks); // 预留空间
printf("scclNodeInfoSet 预留空间\n");
}
void BootstrapComm::init(int rank, int nRanks, int localRank, int nLocalRanks) {
printf("BootstrapComm 构造函数, rank=%d\n", rank);
this->rank = rank;
this->nRanks = nRanks;
this->localRank = localRank;
this->nLocalRanks = nLocalRanks;
node_info_set = new scclNodeInfoSet(nRanks); // 假设需要动态分配
};
void BootstrapComm::destroy() {
printf("BootstrapComm 析构函数, rank=%d\n", rank);
if(node_info_set) {
delete node_info_set; // 释放动态分配的内存
}
}
//////////////////////////////////////////////////////////////////////////////////////////
/** /**
* 计算字符串的哈希值(基于DJB2a算法) * 计算字符串的哈希值(基于DJB2a算法)
* *
...@@ -165,25 +191,40 @@ scclResult_t getBusId(int hipDev, int64_t* busId) { ...@@ -165,25 +191,40 @@ scclResult_t getBusId(int hipDev, int64_t* busId) {
return scclSuccess; return scclSuccess;
} }
// 函数:打印 scclUniqueInfo 结构体的信息 // 函数:打印 scclNodeInfo 结构体的信息
void printUniqueInfo(struct scclUniqueInfo* info) { scclResult_t printNodeInfo(struct scclNodeInfo* info) {
char addrline[net::SOCKET_NAME_MAXLEN + 1];
if(info->localRank == 0) { if(info->localRank == 0) {
printf("\n==========================================\nTotal Rank: %d/%d, Local Rank: %d/%d, CUDA Device ID/Cnt: %d/%d, \n" printf("==========================================\n"
"Host Hash: %lu, PID Hash: %lu, gpu.name=%s, gcn=%s\n" "Total Rank: %d, Local Rank: %d, Host Hash: %lu, PID Hash: %lu\n"
"gpu: dev=%d, gpu.name=%s, gcn=%s, compCap=%d\n"
"net: count=%d, device name=%s, pciPath=%s, guid=%lu, ptrSupport=%d, speed=%d, port=%d, latency=%f, maxComms=%d, maxRecvs=%d\n"
"cpu: socketAddr=%s\n pci: busId=%ld\n"
"\n==========================================\n", "\n==========================================\n",
info->rank, info->rank,
info->nRanks,
info->localRank, info->localRank,
info->localRanks,
info->hipDev,
info->deviceCnt,
info->hostHash, info->hostHash,
info->pidHash, info->pidHash,
info->localNode.gpu.dev,
info->localNode.gpu.name, info->localNode.gpu.name,
info->localNode.gpu.gcn); info->localNode.gpu.gcn,
SCCLCHECK(net::printNetProps(&info->localNode.net.props, info->rank, info->localRank)); info->localNode.gpu.compCap,
info->localNode.net.count,
info->localNode.net.props.name,
info->localNode.net.props.pciPath,
info->localNode.net.props.guid,
info->localNode.net.props.ptrSupport,
info->localNode.net.props.speed,
info->localNode.net.props.port,
info->localNode.net.props.latency,
info->localNode.net.props.maxComms,
info->localNode.net.props.maxRecvs,
net::net_socket::scclSocketToString(&info->localNode.cpu.listen_sock.addr, addrline),
info->localNode.pci.busId);
} }
return;
return scclSuccess;
} }
} // namespace bootstrap } // namespace bootstrap
......
#pragma once #pragma once
#include <string.h> #include <string.h>
#include <cstddef>
#include <vector>
#include "base.h" #include "base.h"
#include "topo_utils.h" #include "topo_utils.h"
#include "comm.h" #include "comm.h"
...@@ -11,28 +13,43 @@ namespace hardware { ...@@ -11,28 +13,43 @@ namespace hardware {
namespace topology { namespace topology {
namespace bootstrap { namespace bootstrap {
typedef net::net_socket::scclSocketAddress scclSocketAddress_t; typedef union net::net_socket::scclSocketAddress scclSocketAddress_t;
typedef net::net_socket::scclSocket scclSocket_t; typedef struct net::net_socket::scclSocket scclSocket_t;
typedef net::scclNet_t scclNet_t; typedef net::scclNet_t scclNet_t;
// scclBootstrapHandle 结构体定义,用于存储引导句柄 // 用于初始化时广播0号rank的地址信息
struct scclBootstrapHandle { struct BootstrapHandle {
uint64_t magic; // 魔术数,用于标识结构体的有效性 uint64_t magic = 0; // 随机码,用于socket通信
scclSocketAddress_t addr; // 地址,用于网络通信 scclSocketAddress_t addr; // 地址,用于网络通信
}; };
// 定义硬件拓扑类型枚举 #define SCCL_UNIQUE_ID_BYTES (40) // sizeof(BootstrapHandle)
typedef enum { typedef struct {
GPU = 0, // 图形处理单元 char internal[SCCL_UNIQUE_ID_BYTES];
PCI = 1, // 外围组件互连 } scclUniqueId;
XGMI = 2, // 非易失性存储器,NV卡中为nvlink
CPU = 3, // 中央处理器,实际上是NUMA域 // 仅用于初始化的函数bootstrapCreateRoot,用于传递detach线程的参数
NIC = 4, // 网络接口控制器 struct bootstrapRootArgs {
NET = 5 // 网络 uint64_t magic;
} topoNodeType_t; scclSocket_t* listenSock = nullptr; // 根节点的监听
};
// 用于初始建立连接阶段,0号rank之外的进程向其传递的信息
struct BootstrapNodeBasic {
int rank;
int nRanks; // 进程的总数量
uint64_t hostHash; // 用于区分host的CPU编号
scclSocketAddress_t addr; // 各个进程的监听套接字地址,用于网络通信
};
// 定义每个rank所持有的所有拓扑节点 // 定义每个rank所持有的所有拓扑节点
struct topoLocalNode { struct topoLocalNode {
struct {
scclSocket_t listen_sock; // 监听套接字
} cpu; // CPU节点
struct {
int64_t busId; // PCI总线ID以int64_t格式表示
} pci; // pci节点
struct { struct {
int dev; // NVML设备编号 int dev; // NVML设备编号
char name[8]; // 设备名称 char name[8]; // 设备名称
...@@ -40,47 +57,48 @@ struct topoLocalNode { ...@@ -40,47 +57,48 @@ struct topoLocalNode {
int compCap; // CUDA计算能力 int compCap; // CUDA计算能力
} gpu; // GPU节点 } gpu; // GPU节点
struct { struct {
scclSocketAddress_t socketAddr; // 网络地址 int count; // 网卡数量
} cpu; // CPU节点
struct {
net::scclNetProperties_t props; net::scclNetProperties_t props;
int count;
} net; // 网络节点 } net; // 网络节点
struct {
int64_t busId; // PCI总线ID以int64_t格式表示
} pci; // pci节点
}; };
// 定义结构体 scclUniqueInfo,用于存储每个rank的通信节点的信息 // 定义结构体 scclNodeInfo,用于存储每个rank的通信节点的信息
struct scclUniqueInfo { struct scclNodeInfo {
struct topoLocalNode localNode; struct topoLocalNode localNode;
int rank = -1; // 当前节点的全局排名
int localRank = -1; // 当前节点在本地计算节点中的排名
int rank; // 当前节点的全局排名 uint64_t hostHash = 0; // 主机哈希值
int nRanks; // 总的节点数量 uint64_t pidHash = 0; // 进程 ID 哈希值
int localRank; // 当前节点在本地计算节点中的排名
int localRanks; // 本地计算节点中的节点总数
int deviceCnt; // 设备数量
int hipDev; // CUDA 设备 ID
uint64_t hostHash; // 主机哈希值
uint64_t pidHash; // 进程 ID 哈希值
}; };
struct scclUniqueInfoSet { // 每个节点的信息
int nUniqueInfo; // 通信节点的数量 struct scclNodeInfoSet {
std::vector<struct scclUniqueInfo*> unique_info_vec; int nUniqueInfos; // 通信节点的数量
std::vector<struct scclNodeInfo> node_info_vec;
// 构造函数声明
scclNodeInfoSet(int nRanks);
}; };
// scclBootstrapComm 结构体定义,用于存储引导通信信息 // BootstrapComm 结构体定义,用于存储引导通信信息
struct scclBootstrapComm { struct BootstrapComm {
scclNet_t* scclNet; void init(int rank, int nRanks, int localRank, int nLocalRanks);
void destroy();
struct scclUniqueInfo* unique_info; // 每个通信节点的基础信息 public:
scclNet_t* scclNet;
struct scclNodeInfoSet* node_info_set;
cpu_set_t cpuAffinity; // CPU亲和性 cpu_set_t cpuAffinity; // CPU亲和性
int WarpSize; int rank = -1; // 当前节点的全局排名
int nRanks = 0; // 总的节点数量
void* bootstrap; // 引导信息 int localRank = -1; // 当前节点在本地计算节点中的排名
int nLocalRanks = 0; // 本地计算节点中的节点总数
int hipDev = -1; // CUDA 设备 ID
int deviceCnt = 0; // 设备数量
// proxy通信
uint64_t magic; // 魔术数,用于验证结构体 uint64_t magic; // 魔术数,用于验证结构体
volatile uint32_t* abortFlag; // 中止标志,非阻塞套接字设置 volatile uint32_t* abortFlag; // 中止标志,非阻塞套接字设置
...@@ -107,7 +125,23 @@ scclResult_t getBusId(int hipDev, int64_t* busId); ...@@ -107,7 +125,23 @@ scclResult_t getBusId(int hipDev, int64_t* busId);
int scclCudaCompCap(void); int scclCudaCompCap(void);
// 打印唯一的拓扑信息 // 打印唯一的拓扑信息
void printUniqueInfo(struct scclUniqueInfo* info); scclResult_t printNodeInfo(struct scclNodeInfo* info);
// 实现类似于std::span的功能,将字节数组转换为类型数组
template <typename T>
class ByteSpan {
public:
ByteSpan(const char* data, std::size_t size) : data_(reinterpret_cast<const T*>(data)), size_(size / sizeof(T)) {}
const T* data() const { return data_; }
std::size_t size() const { return size_; }
const T& operator[](std::size_t index) const { return data_[index]; }
private:
const T* data_;
std::size_t size_;
};
} // namespace bootstrap } // namespace bootstrap
} // namespace topology } // namespace topology
......
...@@ -112,8 +112,7 @@ scclResult_t rocm_smi_getLinkInfo(int srcIndex, int dstIndex, RSMI_IO_LINK_TYPE* ...@@ -112,8 +112,7 @@ scclResult_t rocm_smi_getLinkInfo(int srcIndex, int dstIndex, RSMI_IO_LINK_TYPE*
*count = 1; *count = 1;
if(*rsmi_type == RSMI_IOLINK_TYPE_XGMI && rsmi_weight == 15) { if(*rsmi_type == RSMI_IOLINK_TYPE_XGMI && rsmi_weight == 15) {
*hops = 1; *hops = 1;
// #if defined USE_ROCM_SMI64CONFIG && rocm_smi_VERSION_MAJOR >= 2 // #if defined USE_ROCM_SMI64CONFIG && rocm_smi_VERSION_MAJOR >= 2
#if 1
uint64_t min_bw = 0, max_bw = 0; uint64_t min_bw = 0, max_bw = 0;
rsmi_version_t version; rsmi_version_t version;
ROCMSMICHECK(rsmi_version_get(&version)); ROCMSMICHECK(rsmi_version_get(&version));
...@@ -123,7 +122,6 @@ scclResult_t rocm_smi_getLinkInfo(int srcIndex, int dstIndex, RSMI_IO_LINK_TYPE* ...@@ -123,7 +122,6 @@ scclResult_t rocm_smi_getLinkInfo(int srcIndex, int dstIndex, RSMI_IO_LINK_TYPE*
*count = max_bw / min_bw; *count = max_bw / min_bw;
INFO(SCCL_LOG_GRAPH, "rocm smi srcIndex:%d dstIndex:%d min_bw:%ld max_bw:%ld count:%d", srcIndex, dstIndex, min_bw, max_bw, *count); INFO(SCCL_LOG_GRAPH, "rocm smi srcIndex:%d dstIndex:%d min_bw:%ld max_bw:%ld count:%d", srcIndex, dstIndex, min_bw, max_bw, *count);
#endif
} }
return scclSuccess; return scclSuccess;
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "param.h" #include "param.h"
#include "alloc.h" #include "alloc.h"
#include "utils.h" #include "utils.h"
#include "asm_ops.h"
/* /*
外部环境变量设置: 外部环境变量设置:
...@@ -19,6 +20,7 @@ namespace sccl { ...@@ -19,6 +20,7 @@ namespace sccl {
#define SCCL_MAX_NTHREADS 256 #define SCCL_MAX_NTHREADS 256
#define SCCL_MAX_OPS 2048 #define SCCL_MAX_OPS 2048
#define SCCL_STEPS 8 #define SCCL_STEPS 8
#define SCCL_LOCAL_MAX_NODES 8 // 每个节点内最多的卡数
typedef enum : uint8_t { typedef enum : uint8_t {
scclInt8 = 0, scclInt8 = 0,
...@@ -45,13 +47,4 @@ typedef enum : uint8_t { ...@@ -45,13 +47,4 @@ typedef enum : uint8_t {
SCCL_PROTO_SIMPLE = 2 SCCL_PROTO_SIMPLE = 2
} scclProtocolType_t; } scclProtocolType_t;
// 每个进程的唯一ID
struct scclRankInfo {
int rank; // 当前节点的全局排名
int nRanks; // 总的节点数量
int localRank; // 当前节点的本地rank
int localRanks; // 当前节点的本地rank数量
int hipDev; // CUDA 设备 ID
};
} // namespace sccl } // namespace sccl
...@@ -74,24 +74,24 @@ static const char* scclGetErrorString(scclResult_t code) { ...@@ -74,24 +74,24 @@ static const char* scclGetErrorString(scclResult_t code) {
} \ } \
} while(0); } while(0);
#define SCCLCHECKGOTO(call, RES, label) \ #define SCCLCHECKGOTO(call, RES, label) \
do { \ do { \
RES = call; \ RES = call; \
if(RES != scclSuccess && RES != scclInProgress) { \ if(RES != scclSuccess && RES != scclInProgress) { \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d", __FILE__, __LINE__, RES); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d", __func__, __FILE__, __LINE__, RES); \
goto label; \ goto label; \
} \ } \
} while(0); } while(0);
#define HIPCHECK(cmd) \ #define HIPCHECK(cmd) \
do { \ do { \
hipError_t err = cmd; \ hipError_t err = cmd; \
if(err != hipSuccess) { \ if(err != hipSuccess) { \
char hostname[1024]; \ char hostname[1024]; \
gethostname(hostname, 1024); \ gethostname(hostname, 1024); \
printf("%s: Test HIP failure %s:%d '%s'\n", hostname, __FILE__, __LINE__, hipGetErrorString(err)); \ INFO(SCCL_LOG_CODEALL, "%s: Test HIP failure %s:%d '%s'\n", hostname, hipGetErrorString(err)); \
return scclUnhandledHipError; \ return scclUnhandledHipError; \
} \ } \
} while(0) } while(0)
#define HIPCHECKGOTO(cmd, RES, label) \ #define HIPCHECKGOTO(cmd, RES, label) \
...@@ -106,60 +106,60 @@ static const char* scclGetErrorString(scclResult_t code) { ...@@ -106,60 +106,60 @@ static const char* scclGetErrorString(scclResult_t code) {
////////////////////////////// Value检查 ////////////////////////////// ////////////////////////////// Value检查 //////////////////////////////
#define EQCHECK(statement, value) \ #define EQCHECK(statement, value) \
do { \ do { \
if((statement) == value) { \ if((statement) == value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, scclSystemError, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, scclSystemError, strerror(errno)); \
return scclSystemError; \ return scclSystemError; \
} \ } \
} while(0); } while(0);
#define EQCHECKGOTO(statement, value, RES, label) \ #define EQCHECKGOTO(statement, value, RES, label) \
do { \ do { \
if((statement) == value) { \ if((statement) == value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
RES = scclSystemError; \ RES = scclSystemError; \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, RES, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, RES, strerror(errno)); \
goto label; \ goto label; \
} \ } \
} while(0); } while(0);
#define NEQCHECK(statement, value) \ #define NEQCHECK(statement, value) \
do { \ do { \
if((statement) != value) { \ if((statement) != value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, scclSystemError, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, scclSystemError, strerror(errno)); \
return scclSystemError; \ return scclSystemError; \
} \ } \
} while(0); } while(0);
#define NEQCHECKGOTO(statement, value, RES, label) \ #define NEQCHECKGOTO(statement, value, RES, label) \
do { \ do { \
if((statement) != value) { \ if((statement) != value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
RES = scclSystemError; \ RES = scclSystemError; \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, RES, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, RES, strerror(errno)); \
goto label; \ goto label; \
} \ } \
} while(0); } while(0);
#define LECHECK(statement, value) \ #define LECHECK(statement, value) \
do { \ do { \
if((statement) <= value) { \ if((statement) <= value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, scclSystemError, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, scclSystemError, strerror(errno)); \
return scclSystemError; \ return scclSystemError; \
} \ } \
} while(0); } while(0);
#define LTCHECK(statement, value) \ #define LTCHECK(statement, value) \
do { \ do { \
if((statement) < value) { \ if((statement) < value) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, scclSystemError, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, scclSystemError, strerror(errno)); \
return scclSystemError; \ return scclSystemError; \
} \ } \
} while(0); } while(0);
////////////////////////////// SYS ////////////////////////////// ////////////////////////////// SYS //////////////////////////////
...@@ -190,14 +190,14 @@ static const char* scclGetErrorString(scclResult_t code) { ...@@ -190,14 +190,14 @@ static const char* scclGetErrorString(scclResult_t code) {
} \ } \
} while(true) } while(true)
#define SYSCHECKGOTO(statement, RES, label) \ #define SYSCHECKGOTO(statement, RES, label) \
do { \ do { \
if((statement) == -1) { \ if((statement) == -1) { \
/* Print the back trace*/ \ /* Print the back trace*/ \
RES = scclSystemError; \ RES = scclSystemError; \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __FILE__, __LINE__, RES, strerror(errno)); \ INFO(SCCL_LOG_CODEALL, "%s:%d -> %d (%s)", __func__, __FILE__, __LINE__, RES, strerror(errno)); \
goto label; \ goto label; \
} \ } \
} while(0); } while(0);
} // namespace sccl } // namespace sccl
#pragma once
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <algorithm> // For std::min/std::max
#include "sccl.h"
#ifdef PROFAPI
#define SCCL_API(ret, func, args...) \
__attribute__((visibility("default"))) __attribute__((alias(#func))) ret p##func(args); \
extern "C" __attribute__((visibility("default"))) __attribute__((weak)) ret func(args)
#else
#define SCCL_API(ret, func, args...) extern "C" __attribute__((visibility("default"))) ret func(args)
#endif // end PROFAPI
static __inline__ int scclTypeSize(scclDataType_t type) {
switch(type) {
case scclInt8:
case scclUint8: return 1;
case scclFloat16:
#if defined(RCCL_BFLOAT16)
case scclBfloat16:
#endif
return 2;
case scclInt32:
case scclUint32:
case scclFloat32: return 4;
case scclInt64:
case scclUint64:
case scclFloat64: return 8;
default: return -1;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment