Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
lishen01
Sccl
Commits
571a75b5
Commit
571a75b5
authored
Aug 09, 2025
by
lishen
Browse files
完成全部网络的node建立,以及GPU到GPU的path物理路径搜索
parent
379c4128
Changes
44
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
2274 additions
and
344 deletions
+2274
-344
src/hardware/net/rocm_wrap.cpp
src/hardware/net/rocm_wrap.cpp
+4
-0
src/hardware/net/rocm_wrap.h
src/hardware/net/rocm_wrap.h
+2
-0
src/hardware/topology/bootstrap/bootstrap.cpp
src/hardware/topology/bootstrap/bootstrap.cpp
+555
-153
src/hardware/topology/bootstrap/bootstrap.h
src/hardware/topology/bootstrap/bootstrap.h
+120
-13
src/hardware/topology/bootstrap/bootstrap_utils.cpp
src/hardware/topology/bootstrap/bootstrap_utils.cpp
+0
-64
src/hardware/topology/bootstrap/bootstrap_utils.h
src/hardware/topology/bootstrap/bootstrap_utils.h
+10
-103
src/hardware/topology/bootstrap/physical_links.cpp
src/hardware/topology/bootstrap/physical_links.cpp
+700
-0
src/hardware/topology/bootstrap/physical_links.h
src/hardware/topology/bootstrap/physical_links.h
+88
-0
src/hardware/topology/bootstrap/readme.MD
src/hardware/topology/bootstrap/readme.MD
+169
-0
src/hardware/topology/graph/graph.cpp
src/hardware/topology/graph/graph.cpp
+45
-0
src/hardware/topology/graph/graph.h
src/hardware/topology/graph/graph.h
+34
-0
src/hardware/topology/graph/graph_utils.cpp
src/hardware/topology/graph/graph_utils.cpp
+13
-0
src/hardware/topology/graph/graph_utils.h
src/hardware/topology/graph/graph_utils.h
+18
-0
src/hardware/topology/graph/paths.cpp
src/hardware/topology/graph/paths.cpp
+368
-0
src/hardware/topology/graph/paths.h
src/hardware/topology/graph/paths.h
+52
-0
src/hardware/topology/graph/readme.MD
src/hardware/topology/graph/readme.MD
+20
-0
src/hardware/topology/topo_utils.cpp
src/hardware/topology/topo_utils.cpp
+51
-0
src/hardware/topology/topo_utils.h
src/hardware/topology/topo_utils.h
+17
-4
src/include/base.h
src/include/base.h
+1
-0
src/include/check.h
src/include/check.h
+7
-7
No files found.
src/hardware/net/rocm_wrap.cpp
View file @
571a75b5
...
...
@@ -83,6 +83,7 @@ static void initOnceFunc() {
WARN
(
"pfn_hsa_system_get_info failed with %d"
,
res
);
goto
error
;
}
res
=
pfn_hsa_system_get_info
(
HSA_SYSTEM_INFO_VERSION_MINOR
,
&
version_minor
);
if
(
res
!=
0
)
{
WARN
(
"pfn_hsa_system_get_info failed with %d"
,
res
);
...
...
@@ -162,6 +163,7 @@ static void initOnceFunc() {
*/
initResult
=
scclSuccess
;
return
;
error:
initResult
=
scclSystemError
;
...
...
@@ -182,6 +184,8 @@ scclResult_t rocmLibraryInit() {
return
rocm_wrap
::
initResult
;
}
int64_t
getDmaBufEnable
()
{
return
rocm_wrap
::
scclParamDmaBufEnable
();
}
}
// namespace net
}
// namespace hardware
}
// namespace sccl
src/hardware/net/rocm_wrap.h
View file @
571a75b5
...
...
@@ -25,6 +25,8 @@ DECLARE_ROCM_PFN_EXTERN(hsa_status_string);
// 初始化 ROCm 库
scclResult_t
rocmLibraryInit
(
void
);
// 获取 dma buf
int64_t
getDmaBufEnable
();
}
// namespace net
}
// namespace hardware
...
...
src/hardware/topology/bootstrap/bootstrap.cpp
View file @
571a75b5
#include <unistd.h>
#include <sys/types.h>
#include <string
.h
>
#include <
c
string>
#include <sys/resource.h>
#include <iostream>
#include <iomanip>
...
...
@@ -15,20 +15,6 @@
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
/**
* @brief 执行根节点的数据收集和广播操作
*
* 该函数负责以下操作:
* 1. 设置本地监听服务
* 2. 向根节点发送本节点的基本数据
* 3. 从根节点接收本地rank数量信息
* 4. 当本地rank为0时,从根节点接收所有rank的IP数据
* 5. 将收集到的所有rank数据广播给节点内其他rank
*
* @param send_data_basic 发送给根节点的节点基础数据
* @param recv_data_basic 接收广播数据的缓冲区向量
* @return scclResult_t 返回操作结果,成功返回scclSuccess
*/
namespace
bootstrap
{
////////////////////////////////////////////////////////////////////////////////////////////////////////
...
...
@@ -101,7 +87,7 @@ static scclResult_t basicInit() {
return
scclSuccess
;
}
scclResult_t
bootstrapGetUniqueId
(
struct
BootstrapHandle
*
handle
)
{
scclResult_t
bootstrapGetUniqueId
(
BootstrapHandle
_t
*
handle
)
{
SCCLCHECK
(
basicInit
());
// 在每个进程中设置 handle 的值
getRandomData
(
&
handle
->
magic
,
sizeof
(
handle
->
magic
));
...
...
@@ -152,18 +138,18 @@ static scclResult_t setFilesLimit() {
* @note 函数使用线程池加速消息分发,并通过日志记录关键操作步骤
*/
static
void
*
bootstrapRoot
(
void
*
rargs
)
{
struct
bootstrapRootArgs
*
args
=
(
struct
bootstrapRootArgs
*
)
rargs
;
bootstrapRootArgs
_t
*
args
=
(
bootstrapRootArgs
_t
*
)
rargs
;
scclSocket_t
*
listenSock
=
args
->
listenSock
;
// 用于监听的套接字
uint64_t
magic
=
args
->
magic
;
// 用于验证的魔数
scclResult_t
res
=
scclSuccess
;
// 函数结果
class
ThreadPool
*
pthread_pool
=
nullptr
;
// 用于根节点分发消息的线程池
int
nRanks
=
0
;
// nRanks: 进程总数;
int
nLocalRanks
=
1
;
int
c
=
0
;
// c: 已连接的进程计数
uint64_t
rootHostHash
=
0
;
struct
BootstrapNodeBasic
node_basic
=
{};
// 用于存储扩展信息的结构体
struct
BootstrapNodeBasic
*
all_rank_node_basic
=
nullptr
;
// 所有进程的地址
int
nRanks
=
0
;
// nRanks: 进程总数;
int
nLocalRanks
=
1
;
int
c
=
0
;
// c: 已连接的进程计数
uint64_t
rootHostHash
=
0
;
BootstrapNodeBasic
_t
node_basic
=
{};
// 用于存储扩展信息的结构体
BootstrapNodeBasic
_t
*
all_rank_node_basic
=
nullptr
;
// 所有进程的地址
// 定义一个函数或者一个函数对象,用于执行实际的发送数据操作。在后面执行
auto
send_task
=
[](
BootstrapNodeBasic
&
node_basic
,
uint64_t
magic
,
int
rank
,
void
*
data
,
size_t
size
)
{
...
...
@@ -196,7 +182,7 @@ static void* bootstrapRoot(void* rargs) {
}
// 保存该rank的连接句柄
memcpy
(
all_rank_node_basic
+
node_basic
.
rank
,
&
node_basic
,
sizeof
(
struct
BootstrapNodeBasic
));
memcpy
(
all_rank_node_basic
+
node_basic
.
rank
,
&
node_basic
,
sizeof
(
BootstrapNodeBasic
_t
));
++
c
;
// 增加已连接的进程计数
INFO
(
SCCL_LOG_BOOTSTRAP
,
"Received connect from rank %d total %d/%d"
,
node_basic
.
rank
,
c
,
nRanks
);
// 日志
}
while
(
c
<
nRanks
);
// 当已连接的进程数小于总数时循环
...
...
@@ -222,7 +208,7 @@ static void* bootstrapRoot(void* rargs) {
}
// 等待所有任务完成
while
(
!
pthread_pool
->
allTasksCompleted
())
{
usleep
(
100
0
);
// 每1毫秒检查一次任务完成状态
usleep
(
100
);
// 每1毫秒检查一次任务完成状态
}
// --------------------- 3.给所有localRank==0的rank发送all_rank_node_basic数据 --------------------- //
...
...
@@ -231,12 +217,11 @@ static void* bootstrapRoot(void* rargs) {
int
dst_rank
=
r
*
nLocalRanks
;
// 计算目标rank
auto
dst_node_basic
=
all_rank_node_basic
[
dst_rank
];
net
::
net_socket
::
scclSocketClientManager
client_manager
(
&
dst_node_basic
.
sock
.
addr
,
magic
,
net
::
net_socket
::
scclSocketTypeBootstrap
);
bootstrapNet
::
bootstrapNetSend
(
client_manager
.
getSocket
(),
all_rank_node_basic
,
sizeof
(
struct
BootstrapNodeBasic
)
*
nRanks
);
printf
(
"root send nLocalRanks value to rank=%d
\n
"
,
r
);
bootstrapNet
::
bootstrapNetSend
(
client_manager
.
getSocket
(),
all_rank_node_basic
,
sizeof
(
BootstrapNodeBasic_t
)
*
nRanks
);
}
// 等待所有任务完成
while
(
!
pthread_pool
->
allTasksCompleted
())
{
usleep
(
100
0
);
// 每1毫秒检查一次任务完成状态
usleep
(
100
);
// 每1毫秒检查一次任务完成状态
}
INFO
(
SCCL_LOG_BOOTSTRAP
,
"bootstrap send out all %d handles"
,
nRanks
);
// 日志:发送出所有句柄
...
...
@@ -268,8 +253,8 @@ out:
* @param handle 包含bootstrap配置信息的句柄
* @return 成功返回scclSuccess,失败返回相应的错误码
*/
scclResult_t
bootstrapCreateRoot
(
struct
BootstrapHandle
*
handle
)
{
struct
bootstrapRootArgs
*
args
;
scclResult_t
bootstrapCreateRoot
(
BootstrapHandle
_t
*
handle
)
{
bootstrapRootArgs
_t
*
args
;
pthread_t
thread
;
// 设置根节点socket监听
...
...
@@ -291,12 +276,46 @@ scclResult_t bootstrapCreateRoot(struct BootstrapHandle* handle) {
return
scclSuccess
;
}
////////////////////////////// 结构体定义 //////////////////////////////
// scclRankPhysSet构造函数定义
scclRankPhysSet
::
scclRankPhysSet
(
int
nRanks
,
int
nLocalRanks
)
:
nRanks
(
nRanks
),
nLocalRanks
(
nLocalRanks
),
node_info_total_bytes
(
sizeof
(
scclTopoNode_t
)
*
topoNodeMaxLocalNodes
/
nLocalRanks
)
{
printf
(
"scclRankPhysSet 构造函数
\n
"
);
rank_info_vec
.
reserve
(
nRanks
);
// 预留空间
rank_info_vec
.
clear
();
// 与scclNodeInfo_t中的定义一致
node_info_vec
.
reserve
(
nRanks
*
node_info_total_bytes
);
// 预留空间
node_info_vec
.
clear
();
printf
(
"scclRankPhysSet 预留空间并初始化node_info_vec, nRanks * node_info_total_bytes=%zu
\n
"
,
nRanks
*
node_info_total_bytes
);
}
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
;
this
->
interRank
=
rank
/
nLocalRanks
;
this
->
nInterRanks
=
nRanks
/
nLocalRanks
;
rank_phys_set
=
new
scclRankPhysSet
(
nRanks
,
nLocalRanks
);
// 假设需要动态分配
};
void
BootstrapComm
::
destroy
()
{
printf
(
"BootstrapComm 析构函数, rank=%d
\n
"
,
rank
);
if
(
rank_phys_set
)
{
delete
rank_phys_set
;
// 释放动态分配的内存
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
// 构造函数
Bootstrap
::
Bootstrap
(
const
struct
BootstrapHandle
*
handle
,
int
rank
,
int
nRanks
)
Bootstrap
::
Bootstrap
(
const
BootstrapHandle
_t
*
handle
,
int
rank
,
int
nRanks
)
:
root_handle
(
handle
),
rank
(
rank
),
nRanks
(
nRanks
),
localRank
(
-
1
),
nLocalRanks
(
0
),
socketInitDone
(
false
)
{
printf
(
"Bootstrap 构造函数
\n
"
);
scclCalloc
(
&
all_node_basic
,
nRanks
);
}
Bootstrap
::~
Bootstrap
()
{
...
...
@@ -304,9 +323,11 @@ Bootstrap::~Bootstrap() {
if
(
ipcsocket
)
{
delete
ipcsocket
;
}
if
(
all_node_basic
)
free
(
all_node_basic
);
}
scclResult_t
Bootstrap
::
init
(
struct
BootstrapComm
*
bootstrap_comm
)
{
scclResult_t
Bootstrap
::
init
(
BootstrapComm
_t
*
bootstrap_comm
)
{
// 如果已经初始化,直接返回成功
if
(
asm_ops
::
ld_acquire_sys_global
(
&
socketInitDone
))
return
scclSuccess
;
...
...
@@ -317,13 +338,11 @@ scclResult_t Bootstrap::init(struct BootstrapComm* bootstrap_comm) {
// -------------------------- 1.获取自身基础信息 ----------------------------------- //
SCCLCHECK
(
basicInit
());
// 设置基础信息
struct
BootstrapNodeBasic
node_basic
=
{};
BootstrapNodeBasic
_t
node_basic
=
{};
// -------------------------- 2.设置0号rank搜集的CPU信息和localRank信息 ----------------------------------- //
// 创建根节点的数据收集
std
::
vector
<
struct
BootstrapNodeBasic
>
all_node_basic
;
all_node_basic
.
reserve
(
nRanks
);
SCCLCHECK
(
bootstrapRootGatherAndBroadcast
(
&
node_basic
,
all_node_basic
));
SCCLCHECK
(
bootstrapRootGatherAndBroadcast
(
&
node_basic
));
// -------------------------- 3.设置本地localRank的BootstrapComm信息 ----------------------------------- //
// 初始化BootstrapComm类
...
...
@@ -342,53 +361,123 @@ scclResult_t Bootstrap::init(struct BootstrapComm* bootstrap_comm) {
LTCHECK
(
devices_num
,
0
);
// 检查设备数量是否 devices_num>0
LTCHECK
(
devices_num
,
nLocalRanks
);
// 检查设备数量是否 devices_num>nLocalRanks
bootstrap_comm
->
deviceCnt
=
static_cast
<
int
>
(
devices_num
);
// 将设备数量转换为int并赋值给的deviceCnt
#if 0
printf("devices_num=%d\n", bootstrap_comm->deviceCnt);
#endif
LECHECK
(
devices_num
,
bootstrap_comm
->
hipDev
);
// 检查hipDev是否小于deviceCnt
HIPCHECK
(
hipSetDevice
(
bootstrap_comm
->
hipDev
));
// 设置当前设备为hipDev
LECHECK
(
devices_num
,
bootstrap_comm
->
hipDev
);
// 检查hipDev是否小于deviceCnt
HIPCHECK
(
hipSetDevice
(
bootstrap_comm
->
hipDev
));
// 设置当前设备为hipDev
//////// 设置启动通信的scclNet ////////
// 获取环境变量SCCL_NET_NAME的值,如果不存在则默认使用"IB"
const
char
*
envNetName
=
getenv
(
"SCCL_NET_NAME"
);
char
*
netName
=
(
envNetName
!=
NULL
)
?
strdup
(
envNetName
)
:
strdup
(
"IB"
);
#if 0
printf("netName=%s\n", netName);
#endif
// char* netName = (envNetName != NULL) ? strdup(envNetName) : strdup("IB");
char
*
netName
=
strdup
(
"IB"
);
// 初始化网络和引导网络
SCCLCHECK
(
net
::
scclNetInit
(
netName
,
bootstrap_comm
->
scclNet
));
// 释放分配的网络名称字符串
free
(
netName
);
//////// 初始化唯一信息结构体 ////////
struct
scclNodeInfo
local_node_info
;
// 补充定义
local_node_info
.
hostHash
=
node_basic
.
hostHash
;
SCCLCHECK
(
bootstrapCommInitNodeInfo
(
bootstrap_comm
->
scclNet
,
&
local_node_info
));
// 设置CPU信息
memcpy
(
&
(
local_node_info
.
localNode
.
cpu
.
listen_sock
),
&
(
node_basic
.
sock
),
sizeof
(
scclSocket_t
));
scclRankInfo_t
local_rank_info
;
local_rank_info
.
hostHash
=
node_basic
.
hostHash
;
SCCLCHECK
(
bootstrapCommInitNodeInfo
(
bootstrap_comm
->
scclNet
,
&
local_rank_info
));
memcpy
(
&
(
local_rank_info
.
cpu
.
listen_sock
),
&
(
node_basic
.
sock
),
sizeof
(
scclSocket_t
));
//////// 初始化topo node ////////
scclNodeInfo_t
local_topo_nodes
(
nLocalRanks
);
// 使用ByteSpan替代std::vector,并指定容量为pNodes_len
ByteSpanVector
<
scclTopoNode_t
>
nodes_span
((
void
*
)
local_topo_nodes
.
nodes
,
local_topo_nodes
.
totalByteSize
);
#if 1
printf
(
"devices_num=%d, local_rank_info.net.count=%d
\n
"
,
bootstrap_comm
->
deviceCnt
,
local_rank_info
.
net
.
count
);
#endif
// 遍历所有的GPU的pciPath,添加topo node
for
(
int
r
=
localRank
;
r
<
devices_num
;
r
+=
nLocalRanks
)
{
auto
gpu_path
=
physical_links
::
getGpuPciPath
(
r
);
physical_links
::
generate_topo_nodes
(
gpu_path
,
interRank
,
r
,
nodes_span
);
delete
(
gpu_path
);
}
// 遍历所有的NIC的pciPath,添加topo node
for
(
int
r
=
localRank
;
r
<
local_rank_info
.
net
.
count
;
r
+=
nLocalRanks
)
{
auto
net_path
=
physical_links
::
getNetPciPath
(
bootstrap_comm
->
scclNet
,
r
);
physical_links
::
generate_topo_nodes
(
net_path
,
interRank
,
r
,
nodes_span
);
delete
(
net_path
);
}
#if 0
{
char line[20];
sprintf(line, "11111 print rank=%d", rank);
std::string prefix(line); // 创建prefix字符串
printNodeInfo(prefix, &local_node_info); // 正确的调用方式
if(interRank == 0) {
ByteSpanArray<scclTopoNode_t> nodes_span_array(nodes_span.data(), local_topo_nodes.totalByteSize);
printf("print rank=%d, nodes_span size=%zu\n", rank, nodes_span.size());
char line[30];
sprintf(line, "print rank=%d: ", rank);
for(int i = 0; i < nodes_span.size(); i++) {
printf("============================**============================\n");
physical_links::printTopoNode(nodes_span_array, i, line);
printf("============================**============================\n");
}
}
#endif
#if 0
// 尝试采用软件识别GPU之间互联
for(int i = 0; i < bootstrap_comm->deviceCnt; i++) {
// if(i != bootstrap_comm->hipDev) {
RSMI_IO_LINK_TYPE rsmi_type;
int hops, count;
if(rocm_smi_getLinkInfo(bootstrap_comm->hipDev, i, &rsmi_type, &hops, &count) == scclSuccess) {
printf("rank=%d, i=%d, dev=%d, rsmi_type=%d, hops=%d, count=%d\n", rank, i, bootstrap_comm->hipDev, rsmi_type, hops, count);
// if(rsmi_type == RSMI_IOLINK_TYPE_XGMI && hops <= 2) {
// if(1) {
// char busIdStr[] = "00000000:00:00.0";
// SCCLCHECK(rocm_smi_getDevicePciBusIdString(i, busIdStr, sizeof(busIdStr)));
// char lowerId[16];
// for(int c = 0; c < 16; c++) {
// lowerId[c] = tolower(busIdStr[c]);
// if(busIdStr[c] == 0)
// break;
// }
// }
} else {
printf("rsmi get type fail\n");
}
// }
}
#endif
// -------------------------- 4.BootstrapComm信息的allgather ----------------------------------- //
bootstrapCommAllGather
(
all_node_basic
,
&
local_node_info
,
bootstrap_comm
->
node_info
_set
);
bootstrapCommAllGather
(
&
local_rank_info
,
&
local_topo_nodes
,
bootstrap_comm
->
rank_phys
_set
);
if
(
1
)
{
char
line
[
20
];
sprintf
(
line
,
"print rank=%d"
,
rank
);
std
::
string
prefix
(
line
);
// 创建prefix字符串
for
(
int
r
=
0
;
r
<
nRanks
;
r
++
)
{
struct
scclNodeInfo
node_basic
=
bootstrap_comm
->
node_info_set
->
node_info_vec
[
r
];
printNodeInfo
(
prefix
,
&
node_basic
);
// 正确的调用方式
// TODO: 目前手动将节点内的GPU进行mesh连接,因为无法从/sys/device中获取NIC的拓扑信息,rsmi函数也无法获取NIC的拓扑信息。后续优化
bootstrapNodesLink
(
bootstrap_comm
->
rank_phys_set
->
node_info_vec
.
data
(),
bootstrap_comm
->
rank_phys_set
->
node_info_total_bytes
);
#if 0
if(rank == 1) {
size_t dataLen = bootstrap_comm->rank_phys_set->node_info_total_bytes;
printf("nRanks * bootstrap_comm->rank_phys_set->node_info_total_bytes=%zu, %lu\n", dataLen, nRanks * dataLen);
auto node_info_data = reinterpret_cast<char*>(bootstrap_comm->rank_phys_set->node_info_vec.data());
ByteSpanArray<scclTopoNode_t> nodes_span_all(node_info_data, nRanks * dataLen);
printf("print rank=%d, nodes_span_all size=%zu, scclTopoNode_t size=%zu\n", rank, nodes_span_all.size(), sizeof(scclTopoNode_t));
char line[30];
sprintf(line, "print rank=%d: ", rank);
int node_cnt = 0;
for(int i = 0; i < nodes_span_all.size(); i++) {
if(nodes_span_all[i] && nodes_span_all[i]->type > 0) {
if(i < 64) {
printf("============================&&============================\n");
physical_links::printTopoNode(nodes_span_all, i, line);
printf("============================&&============================\n");
} else if(i < 128) {
printf("============================((============================\n");
physical_links::printTopoNode(nodes_span_all, i, line);
printf("============================))============================\n");
} else {
printf("============================@@============================\n");
physical_links::printTopoNode(nodes_span_all, i, line);
printf("============================@@============================\n");
}
node_cnt += 1;
}
}
printf("print rank=%d, node_cnt=%d\n", rank, node_cnt);
}
#endif
// 设置初始化标志
asm_ops
::
st_release_sys_global
(
&
socketInitDone
,
true
);
...
...
@@ -400,22 +489,20 @@ scclResult_t Bootstrap::init(struct BootstrapComm* bootstrap_comm) {
///////////////////////////////////////////////////////////////////////////
/**
* @brief 执行根节点的
数据收
集和广播操作
* @brief 执行根节点的
聚
集和广播操作
*
* 该函数负责以下操作:
* 1.
设置本地监听服务
* 2. 向根节点发送
本节点的基本
数据
* 3. 从根节点接收
本地rank数量信息
* 4. 当
本地r
ank为0时,从根节点接收所有rank的IP数据
* 5. 将
收集到的所有rank
数据广播给节点内其他rank
* 该函数负责
在bootstrap过程中完成
以下操作:
* 1.
各rank首先设置监听
* 2. 向根节点发送
基础
数据
* 3. 从根节点接收
nLocalRanks值
* 4. 当
localR
ank为0时,从根节点接收所有rank的IP数据
* 5. 将
IP
数据广播给节点内其他rank
*
* @param send_data 发送给根节点的数据指针
* @param recv_data 接收广播数据的缓冲区指针
* @param send_data_basic 指向要发送的基础数据的指针
* @return scclResult_t 返回操作结果,成功返回scclSuccess
*/
scclResult_t
Bootstrap
::
bootstrapRootGatherAndBroadcast
(
struct
BootstrapNodeBasic
*
send
_data_basic
,
std
::
vector
<
struct
BootstrapNodeBasic
>&
recv
_data_basic
)
{
scclResult_t
Bootstrap
::
bootstrapRootGatherAndBroadcast
(
BootstrapNodeBasic
_t
*
send_data_basic
)
{
// 总的需要广播的数据
int
recv_data_basic_size
=
nRanks
*
sizeof
(
struct
BootstrapNodeBasic
);
scclSocketAddress_t
root_addr
=
root_handle
->
addr
;
scclSocketAddress_t
localSocketAddr
=
bootstrapNet
::
getLocalSocketAddr
();
...
...
@@ -433,7 +520,7 @@ scclResult_t Bootstrap::bootstrapRootGatherAndBroadcast(struct BootstrapNodeBasi
// ------------- 2.各个节点向根节点发送数据 ------------- //
{
net
::
net_socket
::
scclSocketClientManager
client_manager
(
&
root_addr
,
root_handle
->
magic
,
net
::
net_socket
::
scclSocketTypeBootstrap
);
SCCLCHECK
(
bootstrapNet
::
bootstrapNetSend
(
client_manager
.
getSocket
(),
send_data_basic
,
sizeof
(
struct
BootstrapNodeBasic
)));
SCCLCHECK
(
bootstrapNet
::
bootstrapNetSend
(
client_manager
.
getSocket
(),
send_data_basic
,
sizeof
(
BootstrapNodeBasic
_t
)));
}
// ------------- 3.从根节点接收nLocalRanks值 ------------- //
// 接收nLocalRanks信息
...
...
@@ -450,15 +537,17 @@ scclResult_t Bootstrap::bootstrapRootGatherAndBroadcast(struct BootstrapNodeBasi
this
->
interRank
=
rank
/
nLocalRanks
;
this
->
nInterRanks
=
nRanks
/
nLocalRanks
;
int
all_node_basic_size
=
nRanks
*
sizeof
(
BootstrapNodeBasic_t
);
// 从根节点接收数据,对应到函数 bootstrapRoot
if
(
localRank
==
0
)
{
net
::
net_socket
::
scclSocketAcceptManager
accept_manager
(
local_server_sock
);
SCCLCHECK
(
bootstrapNet
::
bootstrapNetRecv
(
accept_manager
.
getSocket
(),
recv_data_basic
.
data
(),
recv_data
_basic_size
));
SCCLCHECK
(
bootstrapNet
::
bootstrapNetRecv
(
accept_manager
.
getSocket
(),
all_node_basic
,
all_node
_basic_size
));
}
// ------------- 5.nLocalRanks==0时,将所有rank的ip数据广播给节点内其他rank ------------- //
ipcsocket
=
new
scclIpcSocket_t
(
localRank
,
nLocalRanks
,
/*hash*/
root_handle
->
magic
);
ipcsocket
->
scclIpcSocketBroadcast
(
recv_data_basic
.
data
(),
recv_data
_basic_size
,
/*localRank root*/
0
);
ipcsocket
->
scclIpcSocketBroadcast
(
all_node_basic
,
all_node
_basic_size
,
/*localRank root*/
0
);
return
scclSuccess
;
}
...
...
@@ -466,88 +555,135 @@ scclResult_t Bootstrap::bootstrapRootGatherAndBroadcast(struct BootstrapNodeBasi
/**
* @brief 初始化节点通信信息
*
* 该函数用于初始化节点的通信信息,包括:
* - 设置节点的全局排名和本地排名
* - 获取并设置进程ID哈希值
* - 设置GPU设备属性(名称、GCN架构、计算能力)
* - 设置RDMA网络属性
* - 设置PCI总线ID
* - 设置CPU套接字地址
* 该函数用于初始化节点的通信信息,包括基础信息和硬件信息。
*
* @param scclNet 网络句柄
* @param socket_addr 套接字地址
* @param node_info 节点信息结构体指针
* @param scclNet 网络设备句柄
* @param rank_info 节点信息结构体指针
* @return scclResult_t 返回操作结果,成功返回scclSuccess
*
* @note 基础信息包括:
* - rank: 当前节点的全局排名
* - localRank: 本地计算节点中的排名
* - pidHash: 进程ID哈希值
*
* @note 硬件信息包括:
* - GPU信息: 设备号、名称、GCN架构、计算能力、PCI总线ID
* - RDMA信息: 网卡数量、名称、PCI路径、GUID、指针支持类型、端口速度、端口号、延迟、最大通信数和接收数
*
* @todo 更多硬件信息可参考ncclTopoGetXmlFromSys函数实现
*/
scclResult_t
Bootstrap
::
bootstrapCommInitNodeInfo
(
scclNet_t
*
scclNet
,
s
truct
scclNodeInfo
*
node
_info
)
{
scclResult_t
Bootstrap
::
bootstrapCommInitNodeInfo
(
scclNet_t
*
scclNet
,
s
cclRankInfo_t
*
rank
_info
)
{
////////////////// 设置基础信息 //////////////////
node
_info
->
rank
=
rank
;
// 当前节点的全局排名
node
_info
->
localRank
=
localRank
;
// 当前节点在本地计算节点中的排名
node
_info
->
pidHash
=
getPidHash
();
// 获取进程ID哈希值并赋值给的pidHash
rank
_info
->
rank
=
rank
;
// 当前节点的全局排名
rank
_info
->
localRank
=
localRank
;
// 当前节点在本地计算节点中的排名
rank
_info
->
pidHash
=
getPidHash
();
// 获取进程ID哈希值并赋值给的pidHash
int
hipDev
=
localRank
;
////////////////// 设置硬件信息 //////////////////
struct
topoLocalNode
*
p_localNode
=
&
node_info
->
localNode
;
// 设置PCI信息
SCCLCHECK
(
getBusId
(
hipDev
,
&
p_localNode
->
pci
.
busId
));
// 设置GPU信息
p_localNode
->
gpu
.
dev
=
hipDev
;
//// 1.设置GPU信息
rank_info
->
gpu
.
dev
=
hipDev
;
hipDeviceProp_t
deviceProp
;
HIPCHECK
(
hipGetDeviceProperties
(
&
deviceProp
,
hipDev
));
snprintf
(
p_localNode
->
gpu
.
name
,
sizeof
(
p_localNode
->
gpu
.
name
),
"%s"
,
deviceProp
.
name
);
snprintf
(
p_localNode
->
gpu
.
gcn
,
sizeof
(
p_localNode
->
gpu
.
gcn
),
"%s"
,
deviceProp
.
gcnArchName
);
p_localNode
->
gpu
.
compCap
=
deviceProp
.
major
*
10
+
deviceProp
.
minor
;
// 设置RDMA信息
snprintf
(
rank_info
->
gpu
.
name
,
sizeof
(
rank_info
->
gpu
.
name
),
"%s"
,
deviceProp
.
name
);
snprintf
(
rank_info
->
gpu
.
gcn
,
sizeof
(
rank_info
->
gpu
.
gcn
),
"%s"
,
deviceProp
.
gcnArchName
);
rank_info
->
gpu
.
compCap
=
deviceProp
.
major
*
10
+
deviceProp
.
minor
;
// 设置GPU的busId
SCCLCHECK
(
getBusId
(
hipDev
,
&
rank_info
->
gpu
.
pciBusId
));
// 根据GPU的busId设置pci路径
char
busIdStr
[]
=
"00000000:00:00.0"
;
char
*
gpuPath
=
NULL
;
SCCLCHECK
(
int64ToBusId
(
rank_info
->
gpu
.
pciBusId
,
busIdStr
));
SCCLCHECK
(
getPciPath
(
busIdStr
,
&
gpuPath
));
snprintf
(
rank_info
->
gpu
.
pciPath
,
sizeof
(
rank_info
->
gpu
.
pciPath
),
"%s"
,
gpuPath
);
// 设备在/sys中的路径。
//// 2.设置RDMA信息
net
::
scclNetProperties_t
props
;
SCCLCHECK
(
scclNet
->
getProperties
(
hipDev
,
&
props
));
SCCLCHECK
(
scclNet
->
devices
(
&
p_localNode
->
net
.
count
));
// 节点内网卡数量
snprintf
(
p_localNode
->
net
.
name
,
sizeof
(
p_localNode
->
net
.
name
),
"%s"
,
props
.
name
);
// 主要用于日志记录。
snprintf
(
p_localNode
->
net
.
pciPath
,
sizeof
(
p_localNode
->
net
.
pciPath
),
"%s"
,
props
.
pciPath
);
// PCI设备在/sys中的路径。
SCCLCHECK
(
scclNet
->
devices
(
&
rank_info
->
net
.
count
));
// 节点内网卡数量
snprintf
(
rank_info
->
net
.
name
,
sizeof
(
rank_info
->
net
.
name
),
"%s"
,
props
.
name
);
// 主要用于日志记录。
snprintf
(
rank_info
->
net
.
pciPath
,
sizeof
(
rank_info
->
net
.
pciPath
),
"%s"
,
props
.
pciPath
);
// PCI设备在/sys中的路径。
#if 0
printf("
p_localNode
->net.pciPath len=%zu\n", strlen(
p_localNode
->net.pciPath));
printf("
rank_info
->net.pciPath len=%zu\n", strlen(
rank_info
->net.pciPath));
#endif
p_localNode
->
net
.
guid
=
props
.
guid
;
// NIC芯片的唯一标识符。对于具有多个PCI功能(物理或虚拟)的卡非常重要。
p_localNode
->
net
.
ptrSupport
=
props
.
ptrSupport
;
// [SCCL_PTR_HOST|SCCL_PTR_CUDA|SCCL_PTR_DMABUF]
p_localNode
->
net
.
speed
=
props
.
speed
;
// 端口速度,单位为Mbps。
p_localNode
->
net
.
port
=
props
.
port
;
// 端口号。
p_localNode
->
net
.
latency
=
props
.
latency
;
// 网络延迟
p_localNode
->
net
.
maxComms
=
props
.
maxComms
;
// 可以创建的最大通信数量
p_localNode
->
net
.
maxRecvs
=
props
.
maxRecvs
;
// 最大分组接收数量。
// TODO: 更多硬件信息参考ncclTopoGetXmlFromSys函数写法,可以通过 "/sys/class/drm/card1/device" 等路径读取
rank_info
->
net
.
guid
=
props
.
guid
;
// NIC芯片的唯一标识符。对于具有多个PCI功能(物理或虚拟)的卡非常重要。
rank_info
->
net
.
ptrSupport
=
props
.
ptrSupport
;
// [SCCL_PTR_HOST|SCCL_PTR_CUDA|SCCL_PTR_DMABUF]
rank_info
->
net
.
speed
=
props
.
speed
;
// 端口速度,单位为Mbps。
rank_info
->
net
.
port
=
props
.
port
;
// 端口号。
rank_info
->
net
.
latency
=
props
.
latency
;
// 网络延迟
rank_info
->
net
.
maxComms
=
props
.
maxComms
;
// 可以创建的最大通信数量
rank_info
->
net
.
maxRecvs
=
props
.
maxRecvs
;
// 最大分组接收数量。
return
scclSuccess
;
}
/**
* @brief 实现节点间通信的AllGather操作
*
* 该函数通过调用`bootstrapAllGather`函数,实现节点间通信的AllGather操作。
* 它将每个节点的`rank_info`信息和`node_info`信息收集到`rank_phys_set`中,以便后续使用。
*
* @param rank_info 指向当前节点的`rank_info`信息的指针
* @param node_info 指向当前节点的`node_info`信息的指针
* @param rank_phys_set 指向节点信息集合的指针,用于存储所有节点的`rank_info`和`node_info`信息
* @return scclResult_t 返回操作结果状态码:
* - scclSuccess: 操作成功
* - 其他错误码: 表示操作失败
*
* @note 该函数是一个简化的接口,用于调用`bootstrapAllGather`函数来实现节点间通信的AllGather操作。
* 在调用`bootstrapAllGather`函数之前,需要确保`rank_info`、`node_info`和`rank_phys_set`已经正确初始化。
*/
scclResult_t
Bootstrap
::
bootstrapCommAllGather
(
scclRankInfo_t
*
rank_info
,
scclNodeInfo_t
*
node_info
,
scclRankPhysSet_t
*
rank_phys_set
)
{
SCCLCHECK
(
bootstrapAllGather
(
rank_info
,
rank_phys_set
->
rank_info_vec
.
data
(),
sizeof
(
scclRankInfo_t
)));
SCCLCHECK
(
bootstrapAllGather
(
node_info
->
nodes
,
rank_phys_set
->
node_info_vec
.
data
(),
rank_phys_set
->
node_info_total_bytes
));
return
scclSuccess
;
}
scclResult_t
Bootstrap
::
bootstrapCommAllGather
(
std
::
vector
<
struct
BootstrapNodeBasic
>&
all_node_basic
,
struct
scclNodeInfo
*
node_info
,
struct
scclNodeInfoSet
*
node_info_set
)
{
// TODO: 后续可以采用优化,先节点内allgather,再节点间的allgather,最后节点内的Broadcast。优化的算法并保证正确性
/**
* @brief 实现跨节点的AllGather通信操作
*
* 该函数实现了一个跨节点的AllGather通信操作,包括节点内通信和节点间通信。
* 在节点内通信中,使用IPC套接字进行AllGather操作;在节点间通信中,使用Ring AllGather算法进行数据传输。
* 最后,通过节点内通信的Broadcast操作,将收集到的数据分发给所有节点内的进程。
*
* @param src_data 源数据指针,表示每个节点要发送的数据
* @param dst_data 目标数据指针,表示所有节点收集到的数据将存储在此处
* @param data_size 每个节点要发送的数据大小(以字节为单位)
* @return scclResult_t 返回操作结果状态码:
* - scclSuccess: 操作成功
* - 其他错误码: 表示操作失败
*
* @note 该函数假设所有节点的本地秩(localRank)和节点间秩(interRank)已经正确设置。
* 此外,该函数还假设所有节点的基本信息(如套接字地址)已经通过其他途径正确获取并存储在all_node_basic向量中。
* 在节点间通信中,使用了Ring AllGather算法,该算法在nRanks特别大的时候可能不是最优的选择,可以考虑进一步优化算法以减少通信次数。
*/
scclResult_t
Bootstrap
::
bootstrapAllGather
(
const
void
*
src_data
,
void
*
dst_data
,
int
data_size
)
{
// 数据准备
size_t
inter_data_len
=
nLocalRanks
*
sizeof
(
struct
scclNodeInfo
)
;
// 节点间传输时每个子块的大小
auto
all_recv_data
=
reinterpret_cast
<
char
*>
(
node_info_set
->
node_info_vec
.
data
()
);
size_t
inter_data_len
=
nLocalRanks
*
data_size
;
// 节点间传输时每个子块的大小
auto
all_recv_data
=
reinterpret_cast
<
char
*>
(
dst_
data
);
// 1.节点内通信 allgather
//
//
1.节点内通信 allgather
auto
local_recv_data
=
all_recv_data
+
this
->
interRank
*
inter_data_len
;
ipcsocket
->
scclIpcSocketAllgather
Sync
(
node_info
,
(
void
*
)
local_recv_data
,
sizeof
(
struct
scclNodeInfo
)
);
ipcsocket
->
scclIpcSocketAllgather
(
src_data
,
(
void
*
)
local_recv_data
,
data_size
);
// 2.节点间通信,ring allgather
// TODO: 后续nRanks特别大的时候,可以进一步优化算法,减少通信次数
if
(
nInterRanks
<=
1
)
{
return
scclSuccess
;
}
//// 2.节点间通信,ring allgather
// TODO: 后续nRanks特别大的时候,可以进一步优化算法,减少通信次数。
// 因为节点内信息是已经 allgather了的,所以节点间的传输可以不仅仅依赖localRank == 0,每个localRank都可以用上,即分组
if
(
localRank
==
0
)
{
int
prev_interRank
=
(
this
->
interRank
-
1
+
this
->
nInterRanks
)
%
this
->
nInterRanks
*
this
->
nLocalRanks
;
int
next_interRank
=
(
this
->
interRank
+
1
+
this
->
nInterRanks
)
%
this
->
nInterRanks
*
this
->
nLocalRanks
;
// scclSocket_t prev_rank_sock = all_node_basic[prev_interRank].sock;
scclSocket_t
next_rank_sock
=
all_node_basic
[
next_interRank
].
sock
;
scclSocket_t
self_rank_sock
=
all_node_basic
[
rank
].
sock
;
printf
(
"bootstrap allgather 11: rank %d, prev_interRank=%d, next_interRank=%d
\n
"
,
rank
,
prev_interRank
,
next_interRank
);
// 对于prev,当前rank是客户端;对于next,当前rank是服务器端
//// 对于prev,当前rank是客户端;对于next,当前rank是服务器端
// 客户端:用于发送数据
scclSocket_t
next_rank_sock
=
all_node_basic
[
next_interRank
].
sock
;
net
::
net_socket
::
scclSocketClientManager
client_manager
(
&
next_rank_sock
.
addr
,
root_handle
->
magic
,
net
::
net_socket
::
scclSocketTypeBootstrap
);
// 服务器端:用于接收数据
scclSocket_t
self_rank_sock
=
all_node_basic
[
rank
].
sock
;
net
::
net_socket
::
scclSocketAcceptManager
accept_manager
(
&
self_rank_sock
);
/////////////////// 实现数据传输 ///////////////////
for
(
int
r
=
0
;
r
<
nInterRanks
-
1
;
r
++
)
{
int
prev_rank
=
(
this
->
interRank
-
r
-
1
+
nInterRanks
)
%
nInterRanks
;
...
...
@@ -555,37 +691,303 @@ scclResult_t Bootstrap::bootstrapCommAllGather(std::vector<struct BootstrapNodeB
// 准备发送/接收的数据
auto
send_data
=
all_recv_data
+
next_rank
*
inter_data_len
;
auto
recv_data
=
all_recv_data
+
prev_rank
*
inter_data_len
;
#if 0
printf("bootstrapCommAllGather rank=%d, interRank=%d, r=%d, prev_rank=%d, next_rank=%d, inter_data_len=%zu\n",
this->rank,
this->interRank,
r,
prev_rank,
next_rank,
inter_data_len);
#endif
// 发送/接收数据
SCCLCHECK
(
bootstrapNet
::
bootstrapNetSend
(
client_manager
.
getSocket
(),
send_data
,
inter_data_len
));
SCCLCHECK
(
bootstrapNet
::
bootstrapNetRecv
(
accept_manager
.
getSocket
(),
recv_data
,
inter_data_len
));
}
}
//// 3.节点内通信 broadcast
ipcsocket
->
scclIpcSocketBroadcast
(
all_recv_data
,
nRanks
*
data_size
,
0
);
return
scclSuccess
;
}
// TODO: 当前实现使用了较多的for循环,在节点数量较大时速度较慢,可以考虑采用cuda kernel
/**
* @brief 初始化并连接节点之间的链接
*
* 该函数接收一个指向节点信息的字节数组的指针和节点信息的总字节数,用于初始化并连接节点之间的链接。
* 1.创建一个`ByteSpanArray`对象来管理节点信息的内存,然后根据节点的类型(GPU、PCI或NIC)将它们分类存储。
* 2.它使相同`interRank`下的GPU节点两两互联
* 3.遍历所有的`interRank`来合并具有相同`id`、`type`和`busIdStr`的PCI节点。
* 4.使CPU node即numa node的neighbors两两互联。
* 5.它使相同`deviceId`下的NIC节点两两互联。
*
* @param node_info_vec 指向节点信息的字节数组的指针
* @param node_info_total_bytes 节点信息的总字节数
* @return scclResult_t 返回操作结果状态码:
* - scclSuccess: 操作成功
* - scclError: 操作失败
*/
scclResult_t
Bootstrap
::
bootstrapNodesLink
(
void
*
node_info_vec
,
int
node_info_total_bytes
)
{
// 创建一个ByteSpanArray对象,用于管理节点信息的内存
ByteSpanArray
<
scclTopoNode_t
>
node_info_span
(
node_info_vec
,
nRanks
*
node_info_total_bytes
);
// 用于将nodes的deviceId对应的node
std
::
unordered_map
<
uint64_t
,
std
::
vector
<
scclTopoNode_t
*>>
nodes_map_by_deviceId
;
// 用于将interRank内nodes的deviceSig对应的NIC节点连接
std
::
unordered_map
<
uint64_t
,
std
::
vector
<
scclTopoNode_t
*>>
nic_nodes_by_deviceId
;
// 用于识别并连接节点内的GPU node
std
::
vector
<
std
::
vector
<
scclTopoNode_t
*>>
gpu_nodes_by_interRank
(
nInterRanks
);
// -------------------------- 1.遍历所有的节点信息,记录node -------------------------- //
for
(
size_t
i
=
0
;
i
<
node_info_span
.
size
();
++
i
)
{
scclTopoNode_t
*
node
=
node_info_span
[
i
];
// 跳过空节点、跳过没有busId的节点(如空节点或CPU)
if
(
node
->
type
<=
0
)
{
continue
;
}
uint64_t
id
=
node
->
id
;
int
interRank
;
physical_links
::
getIdComponents
(
id
,
&
interRank
);
uint64_t
deviceSig
=
id
&
0xFFFFFFFFFF
;
// 计算 interRank(24bit) + hipDev(8bit) + deviceId(16bit) + terminalType(8bit) + numaId(8bit)
// 选择type为GPU的节点
if
(
node
->
type
==
GPU
)
{
if
(
interRank
>=
gpu_nodes_by_interRank
.
size
())
{
gpu_nodes_by_interRank
.
resize
(
interRank
+
1
);
}
gpu_nodes_by_interRank
[
interRank
].
push_back
(
node
);
}
else
if
(
node
->
type
==
NIC
)
{
nic_nodes_by_deviceId
[
deviceSig
].
push_back
(
node
);
}
nodes_map_by_deviceId
[
id
].
push_back
(
node
);
}
// 合并id相同和busId相同的node
for
(
auto
&
pair
:
nodes_map_by_deviceId
)
{
auto
&
nodes
=
pair
.
second
;
for
(
size_t
i
=
0
;
i
<
nodes
.
size
();
++
i
)
{
for
(
size_t
j
=
i
+
1
;
j
<
nodes
.
size
();
++
j
)
{
// if(nodes[i]->id == nodes[j]->id && nodes[i]->type == nodes[j]->type && nodes[i]->busId == nodes[j]->busId) {
// SCCLCHECK(nodes[i]->combineNode(nodes[j]));
// }
if
(
nodes
[
i
]
->
id
==
nodes
[
j
]
->
id
)
{
if
(
nodes
[
i
]
->
type
==
nodes
[
j
]
->
type
&&
nodes
[
i
]
->
busId
==
nodes
[
j
]
->
busId
)
{
SCCLCHECK
(
nodes
[
i
]
->
combineNode
(
nodes
[
j
]));
}
else
{
#if 0
printf("222222\n");
if(rank == 0) {
char line[20];
sprintf(line, "print rank=%d", rank);
std::string prefix(line); // 创建prefix字符串
for(int r = 0; r < nRanks; r++) {
struct scclNodeInfo node_basic = node_info_set->node_info_vec[r];
printNodeInfo(prefix, &node_basic); // 正确的调用方式
int tmpi_interRank, tmpi_deviceValue, tmpi_terminalType, tmpi_hipDev, tmpi_numaId;
physical_links::getIdComponents(nodes[i]->id, &tmpi_interRank, &tmpi_deviceValue, &tmpi_terminalType, &tmpi_hipDev, &tmpi_numaId);
int tmpj_interRank, tmpj_deviceValue, tmpj_terminalType, tmpj_hipDev, tmpj_numaId;
physical_links::getIdComponents(nodes[j]->id, &tmpj_interRank, &tmpj_deviceValue, &tmpj_terminalType, &tmpj_hipDev, &tmpj_numaId);
char busIdStr_i[17], busIdStr_j[17];
int64ToBusId(nodes[i]->busId, busIdStr_i);
int64ToBusId(nodes[j]->busId, busIdStr_j);
printf("same Id but different type or busId: %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, busIdStr:%s) and %lu(InterRank:%d, V:%d, T:%d, "
"H:%d, N:%d, busIdStr:%s)\n",
nodes[i]->id,
tmpi_interRank,
tmpi_deviceValue,
tmpi_terminalType,
tmpi_hipDev,
tmpi_numaId,
busIdStr_i,
nodes[j]->id,
tmpj_interRank,
tmpj_deviceValue,
tmpj_terminalType,
tmpj_hipDev,
tmpj_numaId,
busIdStr_j);
#endif
}
}
}
}
}
// 遍历所有的节点信息,将CPU的所有neighbor node两两互联
for
(
size_t
i
=
0
;
i
<
node_info_span
.
size
();
++
i
)
{
scclTopoNode_t
*
node
=
node_info_span
[
i
];
// 跳过空节点、跳过没有busId的节点(如空节点或CPU)
if
(
node
->
type
==
CPU
)
{
for
(
size_t
i
=
0
;
i
<
node
->
neighborCount
;
++
i
)
{
for
(
size_t
j
=
i
+
1
;
j
<
node
->
neighborCount
;
++
j
)
{
// 使用unordered_map来加速查找
auto
it_i
=
nodes_map_by_deviceId
.
find
(
node
->
neighbors
[
i
]);
auto
it_j
=
nodes_map_by_deviceId
.
find
(
node
->
neighbors
[
j
]);
if
(
it_i
!=
nodes_map_by_deviceId
.
end
()
&&
it_j
!=
nodes_map_by_deviceId
.
end
())
{
scclTopoNode_t
*
neighbor_i
=
nullptr
;
scclTopoNode_t
*
neighbor_j
=
nullptr
;
for
(
auto
&
n
:
it_i
->
second
)
{
if
(
n
->
type
>
0
)
{
neighbor_i
=
n
;
break
;
}
}
for
(
auto
&
n
:
it_j
->
second
)
{
if
(
n
->
type
>
0
)
{
neighbor_j
=
n
;
break
;
}
}
if
(
neighbor_i
&&
neighbor_j
)
{
neighbor_i
->
addNeighbor
(
neighbor_j
->
id
);
neighbor_j
->
addNeighbor
(
neighbor_i
->
id
);
#if 0
{
int tmpi_interRank, tmpi_deviceValue, tmpi_terminalType, tmpi_hipDev, tmpi_numaId;
physical_links::getIdComponents(
neighbor_i->id, &tmpi_interRank, &tmpi_deviceValue, &tmpi_terminalType, &tmpi_hipDev, &tmpi_numaId);
int tmpj_interRank, tmpj_deviceValue, tmpj_terminalType, tmpj_hipDev, tmpj_numaId;
physical_links::getIdComponents(
neighbor_j->id, &tmpj_interRank, &tmpj_deviceValue, &tmpj_terminalType, &tmpj_hipDev, &tmpj_numaId);
char busIdStr_i[17], busIdStr_j[17];
int64ToBusId(neighbor_i->busId, busIdStr_i);
int64ToBusId(neighbor_j->busId, busIdStr_j);
printf("connect CPU neighbors %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, busIdStr:%s) and %lu(InterRank:%d, V:%d, T:%d, H:%d, "
"N:%d, busIdStr:%s)\n",
neighbor_i->id,
tmpi_interRank,
tmpi_deviceValue,
tmpi_terminalType,
tmpi_hipDev,
tmpi_numaId,
busIdStr_i,
neighbor_j->id,
tmpj_interRank,
tmpj_deviceValue,
tmpj_terminalType,
tmpj_hipDev,
tmpj_numaId,
busIdStr_j);
}
#endif
}
}
}
}
}
}
// 使相同interRank下的GPU node两两互联
for
(
const
auto
&
nodes
:
gpu_nodes_by_interRank
)
{
for
(
size_t
i
=
0
;
i
<
nodes
.
size
();
++
i
)
{
for
(
size_t
j
=
i
+
1
;
j
<
nodes
.
size
();
++
j
)
{
nodes
[
i
]
->
addNeighbor
(
nodes
[
j
]
->
id
);
nodes
[
j
]
->
addNeighbor
(
nodes
[
i
]
->
id
);
#if 0
{
int tmpi_interRank, tmpi_deviceValue, tmpi_terminalType, tmpi_hipDev, tmpi_numaId;
physical_links::getIdComponents(nodes[i]->id, &tmpi_interRank, &tmpi_deviceValue, &tmpi_terminalType, &tmpi_hipDev, &tmpi_numaId);
int tmpj_interRank, tmpj_deviceValue, tmpj_terminalType, tmpj_hipDev, tmpj_numaId;
physical_links::getIdComponents(nodes[j]->id, &tmpj_interRank, &tmpj_deviceValue, &tmpj_terminalType, &tmpj_hipDev, &tmpj_numaId);
char busIdStr_i[17], busIdStr_j[17];
int64ToBusId(nodes[i]->busId, busIdStr_i);
int64ToBusId(nodes[j]->busId, busIdStr_j);
printf("connect GPU %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, busIdStr:%s) and %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, busIdStr:%s)\n",
nodes[i]->id,
tmpi_interRank,
tmpi_deviceValue,
tmpi_terminalType,
tmpi_hipDev,
tmpi_numaId,
busIdStr_i,
nodes[j]->id,
tmpj_interRank,
tmpj_deviceValue,
tmpj_terminalType,
tmpj_hipDev,
tmpj_numaId,
busIdStr_j);
}
#endif
}
}
}
// 3.节点内通信 broadcast
ipcsocket
->
scclIpcSocketBroadcast
(
all_recv_data
,
nRanks
*
sizeof
(
struct
scclNodeInfo
),
0
);
// 使相同deviceId下的NIC节点两两互联
for
(
const
auto
&
pair
:
nic_nodes_by_deviceId
)
{
const
auto
&
nodes
=
pair
.
second
;
for
(
size_t
i
=
0
;
i
<
nodes
.
size
();
++
i
)
{
for
(
size_t
j
=
i
+
1
;
j
<
nodes
.
size
();
++
j
)
{
// 在deviceId相同的情况下,比较busIdStr
if
(
nodes
[
i
]
->
busId
==
nodes
[
j
]
->
busId
)
{
nodes
[
i
]
->
addNeighbor
(
nodes
[
j
]
->
id
);
nodes
[
j
]
->
addNeighbor
(
nodes
[
i
]
->
id
);
#if 0
{
int tmpi_interRank, tmpi_deviceValue, tmpi_terminalType, tmpi_hipDev, tmpi_numaId;
physical_links::getIdComponents(nodes[i]->id, &tmpi_interRank, &tmpi_deviceValue, &tmpi_terminalType, &tmpi_hipDev, &tmpi_numaId);
int tmpj_interRank, tmpj_deviceValue, tmpj_terminalType, tmpj_hipDev, tmpj_numaId;
physical_links::getIdComponents(nodes[j]->id, &tmpj_interRank, &tmpj_deviceValue, &tmpj_terminalType, &tmpj_hipDev, &tmpj_numaId);
char busIdStr_i[17], busIdStr_j[17];
int64ToBusId(nodes[i]->busId, busIdStr_i);
int64ToBusId(nodes[j]->busId, busIdStr_j);
printf("connect NIC interRank %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, busIdStr:%s) and %lu(InterRank:%d, V:%d, T:%d, H:%d, N:%d, "
"busIdStr:%s)\n",
nodes[i]->id,
tmpi_interRank,
tmpi_deviceValue,
tmpi_terminalType,
tmpi_hipDev,
tmpi_numaId,
busIdStr_i,
nodes[j]->id,
tmpj_interRank,
tmpj_deviceValue,
tmpj_terminalType,
tmpj_hipDev,
tmpj_numaId,
busIdStr_j);
}
#endif
}
}
}
}
return
scclSuccess
;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数:打印 scclRankInfo 结构体的信息
scclResult_t
printRankInfo
(
const
std
::
string
&
prefix
,
scclRankInfo_t
*
info
)
{
char
addrline
[
net
::
SOCKET_NAME_MAXLEN
+
1
];
// if(info->localRank == 0) {
if
(
1
)
{
// 将GPU的pciBusId转换为字符串格式
char
busIdhip
[
16
];
SCCLCHECK
(
int64ToBusId
(
info
->
gpu
.
pciBusId
,
busIdhip
));
printf
(
"==========================================
\n
"
"%s, Total Rank: %d, Local Rank: %d, Host Hash: %lu, PID Hash: %lu
\n
"
"gpu: dev=%d, gpu.name=%s, gcn=%s, compCap=%d
\n
"
"gpu: busId=%s, gpuPath=%s
\n
"
"net: count=%d, device name=%s, pciPath=%s, guid=%lu, ptrSupport=%u, speed=%d, port=%d, latency=%f, maxComms=%d, maxRecvs=%d
\n
"
"cpu: socketAddr=%s
\n
pci: busId=%ld"
"
\n
==========================================
\n
"
,
prefix
.
c_str
(),
info
->
rank
,
info
->
localRank
,
info
->
hostHash
,
info
->
pidHash
,
info
->
gpu
.
dev
,
info
->
gpu
.
name
,
info
->
gpu
.
gcn
,
info
->
gpu
.
compCap
,
busIdhip
,
info
->
gpu
.
pciPath
,
info
->
net
.
count
,
info
->
net
.
name
,
info
->
net
.
pciPath
,
info
->
net
.
guid
,
static_cast
<
unsigned
int
>
(
info
->
net
.
ptrSupport
),
info
->
net
.
speed
,
info
->
net
.
port
,
info
->
net
.
latency
,
info
->
net
.
maxComms
,
info
->
net
.
maxRecvs
,
net
::
net_socket
::
scclSocketToString
(
&
info
->
cpu
.
listen_sock
.
addr
,
addrline
),
info
->
gpu
.
pciBusId
);
}
return
scclSuccess
;
}
...
...
src/hardware/topology/bootstrap/bootstrap.h
View file @
571a75b5
...
...
@@ -7,37 +7,139 @@
#include "bootstrap_net.h"
#include "thread_pool.h"
#include "ipc_socket.h"
#include "physical_links.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
bootstrap
{
typedef
class
sccl
::
hardware
::
net
::
ipc_socket
::
scclIpcSocket
scclIpcSocket_t
;
typedef
sccl
::
hardware
::
net
::
ipc_socket
::
scclIpcSocket_t
scclIpcSocket_t
;
typedef
physical_links
::
scclTopoNode_t
scclTopoNode_t
;
///////////////////////////////////// 用于初始化时的功能函数 //////////////////////////////////////////
scclResult_t
bootstrapGetUniqueId
(
struct
BootstrapHandle
*
handle
);
scclResult_t
bootstrapCreateRoot
(
struct
BootstrapHandle
*
handle
);
scclResult_t
bootstrapGetUniqueId
(
BootstrapHandle_t
*
handle
);
scclResult_t
bootstrapCreateRoot
(
BootstrapHandle_t
*
handle
);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 定义结构体 scclRankInfo,用于存储每个rank的通信节点的信息
// 每个rank必定选定一个GPU、一个RDMA设备、一个CPU,作为通信节点
typedef
struct
scclRankInfo
{
struct
{
scclSocket_t
listen_sock
;
// 监听套接字
}
cpu
;
// CPU节点
struct
{
int
dev
;
// NVML设备编号
char
name
[
8
];
// 设备名称
char
gcn
[
8
];
// GCN架构名称
int
compCap
;
// CUDA计算能力
int64_t
pciBusId
;
// PCI总线ID以int64_t格式表示
char
pciPath
[
128
];
// PCI设备在/sys中的路径。
}
gpu
;
// GPU节点
struct
{
int
count
;
// 网卡数量
char
name
[
8
];
// 主要用于日志记录。
char
pciPath
[
128
];
// PCI设备在/sys中的路径。
uint64_t
guid
;
// NIC芯片的唯一标识符。对于具有多个PCI功能(物理或虚拟)的卡非常重要。
uint8_t
ptrSupport
;
// [SCCL_PTR_HOST|SCCL_PTR_CUDA|SCCL_PTR_DMABUF]
int
speed
;
// 端口速度,单位为Mbps。
int
port
;
// 端口号。
float
latency
;
// 网络延迟
int
maxComms
;
// 可以创建的最大通信数量
int
maxRecvs
;
// 最大分组接收数量。
}
net
;
// 网络节点
int
rank
=
-
1
;
// 当前节点的全局排名
int
localRank
=
-
1
;
// 当前节点在本地计算节点中的排名
uint64_t
hostHash
=
0
;
// 主机哈希值
uint64_t
pidHash
=
0
;
// 进程 ID 哈希值
}
scclRankInfo_t
;
// 定义结构体 scclNodeInfo,用于存储每个rank的图连接信息
// TODO: 目前每个rank需要的node_info大小为4k+,当卡数较大时占用内存较大,可以优化。或者不作为全局变量
typedef
struct
scclNodeInfo
{
scclTopoNode_t
*
nodes
;
// 指向scclTopoNode_t对象数组的指针
int
nLocalRanks
;
int
totalByteSize
;
// 表示占用的总字节数
// 带参数的构造函数,用于初始化nodes的大小
scclNodeInfo
(
int
nLocalRanks
)
:
nodes
(
nullptr
),
nLocalRanks
(
nLocalRanks
),
totalByteSize
(
sizeof
(
scclTopoNode_t
)
*
topoNodeMaxLocalNodes
/
nLocalRanks
)
{
nodes
=
reinterpret_cast
<
scclTopoNode_t
*>
(
malloc
(
totalByteSize
));
if
(
nodes
)
{
memset
(
nodes
,
0
,
totalByteSize
);
}
}
// 析构函数,用于释放申请的数组空间
virtual
~
scclNodeInfo
()
{
if
(
nodes
)
{
free
(
nodes
);
}
}
}
scclNodeInfo_t
;
// 所有节点的信息
typedef
struct
scclRankPhysSet
{
// 构造函数声明
scclRankPhysSet
(
int
nRanks
,
int
nLocalRanks
);
std
::
vector
<
scclRankInfo_t
>
rank_info_vec
;
std
::
vector
<
char
>
node_info_vec
;
// 实际为std::vector<scclNodeInfo_t>,vector不支持scclNodeInfo_t变长
public:
int
nRanks
=
0
;
// 总的节点数量
int
nLocalRanks
=
0
;
// 本地计算节点中的节点总数
size_t
node_info_total_bytes
=
0
;
// 记录可变长度scclNodeInfo_t类型数据的实际大小
}
scclRankPhysSet_t
;
// BootstrapComm 结构体定义,用于存储引导通信信息
typedef
struct
BootstrapComm
{
void
init
(
int
rank
,
int
nRanks
,
int
localRank
,
int
nLocalRanks
);
void
destroy
();
public:
scclNet_t
*
scclNet
=
nullptr
;
scclRankPhysSet_t
*
rank_phys_set
=
nullptr
;
cpu_set_t
cpuAffinity
;
// CPU亲和性
int
rank
=
-
1
;
// 当前节点的全局排名
int
nRanks
=
0
;
// 总的节点数量
int
localRank
=
-
1
;
// 当前节点在本地计算节点中的排名
int
nLocalRanks
=
0
;
// 本地计算节点中的节点总数
int
interRank
=
-
1
;
// 整个节点在全部节点中的位置
int
nInterRanks
=
0
;
// 全局拥有节点的个数
int
hipDev
=
-
1
;
// CUDA 设备 ID
int
deviceCnt
=
0
;
// 设备数量
uint64_t
magic
;
// 魔术数,用于验证结构体
}
BootstrapComm_t
;
///////////////////////////////////// 用于初始化时的类 //////////////////////////////////////////
class
Bootstrap
{
public:
Bootstrap
(
const
struct
BootstrapHandle
*
,
int
rank
,
int
nRanks
);
Bootstrap
(
const
BootstrapHandle
_t
*
,
int
rank
,
int
nRanks
);
~
Bootstrap
();
// 初始化bootstrap通信环境
scclResult_t
init
(
struct
BootstrapComm
*
bootstrap_comm
);
scclResult_t
init
(
BootstrapComm_t
*
bootstrap_comm
);
// 实现跨节点的AllGather通信操作
scclResult_t
bootstrapAllGather
(
const
void
*
src_data
,
void
*
dst_data
,
int
data_size
);
private:
//
创建
根节点的
数据广播
scclResult_t
bootstrapRootGatherAndBroadcast
(
struct
BootstrapNodeBasic
*
send
_data_basic
,
std
::
vector
<
struct
BootstrapNodeBasic
>&
recv
_data_basic
);
//
执行
根节点的
聚集和广播操作
scclResult_t
bootstrapRootGatherAndBroadcast
(
BootstrapNodeBasic
_t
*
send_data_basic
);
// 初始化
唯一ID信息结构体
scclResult_t
bootstrapCommInitNodeInfo
(
scclNet_t
*
scclNet
,
s
truct
scclNodeInfo
*
node
_info
);
// 初始化
节点通信信息
scclResult_t
bootstrapCommInitNodeInfo
(
scclNet_t
*
scclNet
,
s
cclRankInfo_t
*
rank
_info
);
// 广播节点信息
scclResult_t
bootstrapCommAllGather
(
std
::
vector
<
struct
BootstrapNodeBasic
>&
all_node_basic
,
struct
scclNodeInfo
*
node_info
,
struct
scclNodeInfoSet
*
node_info_set
);
// 实现rank_info信息的节点间通信的AllGather操作
scclResult_t
bootstrapCommAllGather
(
scclRankInfo_t
*
rank_info
,
scclNodeInfo_t
*
node_info
,
scclRankPhysSet_t
*
rank_phys_set
);
// 额外处理nRanks个nodes的连接关系
scclResult_t
bootstrapNodesLink
(
void
*
node_info_vec
,
int
node_info_total_bytes
);
private:
int
rank
,
nRanks
;
// 初始化阶段获取MPI的值
...
...
@@ -48,7 +150,9 @@ private:
volatile
uint32_t
*
abortFlag
;
// 中止标志,非阻塞套接字设置
// 外部传入的0号节点的基础信息
const
struct
BootstrapHandle
*
root_handle
;
const
BootstrapHandle_t
*
root_handle
;
// 节点内所有进程的基础ip信息
BootstrapNodeBasic_t
*
all_node_basic
=
nullptr
;
// 初始化标志
bool
socketInitDone
;
...
...
@@ -60,6 +164,9 @@ private:
scclIpcSocket_t
*
ipcsocket
=
nullptr
;
// 指向scclIpcSocket类实例的指针,初始值为nullptr
};
// 打印唯一的拓扑信息
scclResult_t
printRankInfo
(
const
std
::
string
&
prefix
,
scclRankInfo_t
*
info
);
}
// namespace bootstrap
}
// namespace topology
}
// namespace hardware
...
...
src/hardware/topology/bootstrap/bootstrap_utils.cpp
View file @
571a75b5
...
...
@@ -7,32 +7,6 @@ namespace hardware {
namespace
topology
{
namespace
bootstrap
{
////////////////////////////// 结构体定义 //////////////////////////////
// 构造函数定义
scclNodeInfoSet
::
scclNodeInfoSet
(
int
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
;
this
->
interRank
=
rank
/
nLocalRanks
;
this
->
nInterRanks
=
nRanks
/
nLocalRanks
;
node_info_set
=
new
scclNodeInfoSet
(
nRanks
);
// 假设需要动态分配
};
void
BootstrapComm
::
destroy
()
{
printf
(
"BootstrapComm 析构函数, rank=%d
\n
"
,
rank
);
if
(
node_info_set
)
{
delete
node_info_set
;
// 释放动态分配的内存
}
}
//////////////////////////////////////////////////////////////////////////////////////////
/**
...
...
@@ -193,44 +167,6 @@ scclResult_t getBusId(int hipDev, int64_t* busId) {
return
scclSuccess
;
}
// 函数:打印 scclNodeInfo 结构体的信息
scclResult_t
printNodeInfo
(
const
std
::
string
&
prefix
,
struct
scclNodeInfo
*
info
)
{
char
addrline
[
net
::
SOCKET_NAME_MAXLEN
+
1
];
// if(info->localRank == 0) {
if
(
1
)
{
printf
(
"==========================================
\n
"
"%s, 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=%u, speed=%d, port=%d, latency=%f, maxComms=%d, maxRecvs=%d
\n
"
"cpu: socketAddr=%s
\n
pci: busId=%ld"
"
\n
==========================================
\n
"
,
prefix
.
c_str
(),
info
->
rank
,
info
->
localRank
,
info
->
hostHash
,
info
->
pidHash
,
info
->
localNode
.
gpu
.
dev
,
info
->
localNode
.
gpu
.
name
,
info
->
localNode
.
gpu
.
gcn
,
info
->
localNode
.
gpu
.
compCap
,
info
->
localNode
.
net
.
count
,
info
->
localNode
.
net
.
name
,
info
->
localNode
.
net
.
pciPath
,
info
->
localNode
.
net
.
guid
,
static_cast
<
unsigned
int
>
(
info
->
localNode
.
net
.
ptrSupport
),
info
->
localNode
.
net
.
speed
,
info
->
localNode
.
net
.
port
,
info
->
localNode
.
net
.
latency
,
info
->
localNode
.
net
.
maxComms
,
info
->
localNode
.
net
.
maxRecvs
,
net
::
net_socket
::
scclSocketToString
(
&
info
->
localNode
.
cpu
.
listen_sock
.
addr
,
addrline
),
info
->
localNode
.
pci
.
busId
);
}
return
scclSuccess
;
}
}
// namespace bootstrap
}
// namespace topology
}
// namespace hardware
...
...
src/hardware/topology/bootstrap/bootstrap_utils.h
View file @
571a75b5
#pragma once
#include <string.h>
#include <map>
#include <string>
#include <cstddef>
#include <vector>
#include "base.h"
...
...
@@ -17,106 +18,31 @@ typedef union net::net_socket::scclSocketAddress scclSocketAddress_t;
typedef
struct
net
::
net_socket
::
scclSocket
scclSocket_t
;
typedef
net
::
scclNet_t
scclNet_t
;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 用于初始化时广播0号rank的地址信息
struct
BootstrapHandle
{
typedef
struct
BootstrapHandle
{
uint64_t
magic
=
0
;
// 随机码,用于socket通信
scclSocketAddress_t
addr
;
// 地址,用于网络通信
};
}
BootstrapHandle_t
;
#define SCCL_UNIQUE_ID_BYTES (40) // sizeof(BootstrapHandle)
#define SCCL_UNIQUE_ID_BYTES (40) // sizeof(BootstrapHandle
_t
)
typedef
struct
{
char
internal
[
SCCL_UNIQUE_ID_BYTES
];
}
scclUniqueId
;
// 仅用于初始化的函数bootstrapCreateRoot,用于传递detach线程的参数
struct
bootstrapRootArgs
{
typedef
struct
bootstrapRootArgs
{
uint64_t
magic
;
scclSocket_t
*
listenSock
=
nullptr
;
// 根节点的监听
};
}
bootstrapRootArgs_t
;
// 用于初始建立连接阶段,0号rank之外的进程向其传递的信息
struct
BootstrapNodeBasic
{
typedef
struct
BootstrapNodeBasic
{
int
rank
;
int
nRanks
;
// 进程的总数量
uint64_t
hostHash
;
// 用于区分host的CPU编号
scclSocket_t
sock
;
// 各个进程的监听套接字地址,用于网络通信
};
// 定义每个rank所持有的所有拓扑节点
struct
topoLocalNode
{
struct
{
scclSocket_t
listen_sock
;
// 监听套接字
}
cpu
;
// CPU节点
struct
{
int64_t
busId
;
// PCI总线ID以int64_t格式表示
}
pci
;
// pci节点
struct
{
int
dev
;
// NVML设备编号
char
name
[
8
];
// 设备名称
char
gcn
[
8
];
// GCN架构名称
int
compCap
;
// CUDA计算能力
}
gpu
;
// GPU节点
struct
{
int
count
;
// 网卡数量
char
name
[
8
];
// 主要用于日志记录。
char
pciPath
[
128
];
// PCI设备在/sys中的路径。
uint64_t
guid
;
// NIC芯片的唯一标识符。对于具有多个PCI功能(物理或虚拟)的卡非常重要。
uint8_t
ptrSupport
;
// [SCCL_PTR_HOST|SCCL_PTR_CUDA|SCCL_PTR_DMABUF]
int
speed
;
// 端口速度,单位为Mbps。
int
port
;
// 端口号。
float
latency
;
// 网络延迟
int
maxComms
;
// 可以创建的最大通信数量
int
maxRecvs
;
// 最大分组接收数量。
}
net
;
// 网络节点
};
// 定义结构体 scclNodeInfo,用于存储每个rank的通信节点的信息
struct
scclNodeInfo
{
struct
topoLocalNode
localNode
;
int
rank
=
-
1
;
// 当前节点的全局排名
int
localRank
=
-
1
;
// 当前节点在本地计算节点中的排名
uint64_t
hostHash
=
0
;
// 主机哈希值
uint64_t
pidHash
=
0
;
// 进程 ID 哈希值
};
// 每个节点的信息
struct
scclNodeInfoSet
{
std
::
vector
<
struct
scclNodeInfo
>
node_info_vec
;
// 构造函数声明
scclNodeInfoSet
(
int
nRanks
);
};
// BootstrapComm 结构体定义,用于存储引导通信信息
struct
BootstrapComm
{
void
init
(
int
rank
,
int
nRanks
,
int
localRank
,
int
nLocalRanks
);
void
destroy
();
public:
scclNet_t
*
scclNet
;
struct
scclNodeInfoSet
*
node_info_set
;
cpu_set_t
cpuAffinity
;
// CPU亲和性
int
rank
=
-
1
;
// 当前节点的全局排名
int
nRanks
=
0
;
// 总的节点数量
int
localRank
=
-
1
;
// 当前节点在本地计算节点中的排名
int
nLocalRanks
=
0
;
// 本地计算节点中的节点总数
int
interRank
=
-
1
;
// 整个节点在全部节点中的位置
int
nInterRanks
=
0
;
// 全局拥有节点的个数
int
hipDev
=
-
1
;
// CUDA 设备 ID
int
deviceCnt
=
0
;
// 设备数量
// proxy通信
uint64_t
magic
;
// 魔术数,用于验证结构体
volatile
uint32_t
*
abortFlag
;
// 中止标志,非阻塞套接字设置
// int splitShare; // 是否使用共享内存进行分割
// int* topParentRanks; // 顶级父节点的rank
// /* 与代理相关的共享资源 */
// struct scclProxyState* proxyState;
};
}
BootstrapNodeBasic_t
;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 获取主机唯一标识的哈希值,该哈希值在裸机和容器实例中都是唯一的
...
...
@@ -134,25 +60,6 @@ scclResult_t getBusId(int hipDev, int64_t* busId);
// 获取当前HIP设备的计算能力版本号
int
scclCudaCompCap
(
void
);
// 打印唯一的拓扑信息
scclResult_t
printNodeInfo
(
const
std
::
string
&
prefix
,
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 topology
}
// namespace hardware
...
...
src/hardware/topology/bootstrap/physical_links.cpp
0 → 100644
View file @
571a75b5
#include <cstring> // 为了使用strlen
#include "physical_links.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
bootstrap
{
namespace
physical_links
{
constexpr
int
numaIdStrLen
=
10
;
constexpr
int
speedStrLen
=
20
;
constexpr
int
widthStrLen
=
10
;
constexpr
int
busIdStrLen
=
17
;
// 定义一个映射,用于将PCI类代码映射到设备类型
static
std
::
map
<
std
::
string
,
int
>
pciClassMap
=
{
{
"0x060400"
,
PCI
},
{
"0x068000"
,
NVS
},
{
"0x068001"
,
CPU
},
{
"0x03"
,
GPU
},
{
"0x02"
,
NIC
},
{
"0x120000"
,
GPU
},
{
"0x0b4000"
,
GPU
}};
// 定义一个映射,用于将PCI代代码映射到带宽
static
std
::
map
<
std
::
string
,
int
>
pciSpeedMap
=
{{
"2.5 GT/s"
,
15
},
{
"5 GT/s"
,
30
},
{
"8 GT/s"
,
60
},
{
"16 GT/s"
,
120
},
{
"32 GT/s"
,
240
},
// Kernel 5.6 and earlier
{
"2.5 GT/s PCIe"
,
15
},
{
"5.0 GT/s PCIe"
,
30
},
{
"8.0 GT/s PCIe"
,
60
},
{
"16.0 GT/s PCIe"
,
120
},
{
"32.0 GT/s PCIe"
,
240
},
{
"64.0 GT/s PCIe"
,
480
}};
/**
* 检查给定路径是否为有效的PCI设备路径
*
* @param pciPath 要检查的PCI设备路径
* @return true 如果路径包含PCI设备所需的class/device/max_link_speed/max_link_width文件
* @return false 如果路径不包含上述文件
*/
static
bool
isPciDevice
(
const
std
::
string
&
pciPath
)
{
// 检查type和device等文件是否存在
if
(
std
::
filesystem
::
exists
(
pciPath
+
"class"
)
&&
std
::
filesystem
::
exists
(
pciPath
+
"device"
)
&&
std
::
filesystem
::
exists
(
pciPath
+
"max_link_speed"
)
&&
std
::
filesystem
::
exists
(
pciPath
+
"max_link_width"
)
&&
std
::
filesystem
::
exists
(
pciPath
+
"numa_node"
))
{
return
true
;
}
return
false
;
}
/**
* @brief 获取给定字符串中通过指定分隔符分割的最后一个非空子字符串。
*
* 该函数从给定字符串的末尾开始,查找由指定分隔符分割的最后一个非空子字符串。
* 如果找到的子字符串为空,则继续向前查找,直到找到一个非空的子字符串或回到字符串的开头。
* 如果最后依然没有找到非空的子字符串,则返回输入的字符串。
*
* @param str 要查找的字符串。
* @param delimiter 用于分割字符串的分隔符。
* @return std::string 最后一个非空的子字符串。如果没有找到,则返回输入的字符串。
*/
static
std
::
string
getLastNonEmptySegment
(
const
std
::
string
&
str
,
char
delimiter
)
{
size_t
lastSlashPos
=
str
.
find_last_of
(
delimiter
);
while
(
lastSlashPos
!=
std
::
string
::
npos
)
{
size_t
start
=
lastSlashPos
+
1
;
size_t
end
=
str
.
find
(
delimiter
,
start
);
std
::
string
segment
=
str
.
substr
(
start
,
end
-
start
);
if
(
!
segment
.
empty
())
{
return
segment
;
}
lastSlashPos
=
str
.
find_last_of
(
delimiter
,
lastSlashPos
-
1
);
}
return
str
;
// 如果没有找到非空的子字符串,返回原始字符串
}
/**
* @brief 在映射表中查找与给定键最接近的匹配项
*
* 通过比较键的前缀长度来寻找最佳匹配,返回具有最长公共前缀的键。
*
* @tparam T 映射表值的类型
* @param key 要匹配的键
* @param map 要搜索的映射表
* @return std::string 返回映射表中与输入键具有最长公共前缀的键
*/
template
<
typename
T
>
static
std
::
string
findClosestMatch
(
const
std
::
string
&
key
,
const
std
::
map
<
std
::
string
,
T
>&
map
)
{
std
::
string
bestMatch
;
size_t
longestCommonPrefix
=
0
;
for
(
const
auto
&
pair
:
map
)
{
size_t
commonPrefix
=
0
;
while
(
commonPrefix
<
key
.
size
()
&&
commonPrefix
<
pair
.
first
.
size
()
&&
key
[
commonPrefix
]
==
pair
.
first
[
commonPrefix
])
{
++
commonPrefix
;
}
if
(
commonPrefix
>
longestCommonPrefix
)
{
longestCommonPrefix
=
commonPrefix
;
bestMatch
=
pair
.
first
;
}
}
return
bestMatch
;
}
// 定义一个函数,用于从指定路径下的"class"文件中读取设备类型字符串,并根据查找到的匹配项,获取设备类型
static
int
getDeviceTypeFromPciPath
(
const
char
*
pciPath
)
{
char
typeStr
[
9
];
scclTopoGetStrFromSys
(
pciPath
,
"class"
,
typeStr
);
std
::
string
class_map_key
=
findClosestMatch
(
std
::
string
(
typeStr
),
pciClassMap
);
return
pciClassMap
[
class_map_key
];
}
// 根据节点ID在ByteSpan中查找节点
static
scclTopoNode_t
*
findNodeById
(
uint64_t
id
,
ByteSpanArray
<
scclTopoNode_t
>&
nodes
)
{
for
(
size_t
i
=
0
;
i
<
nodes
.
size
();
++
i
)
{
if
(
nodes
[
i
]
->
id
==
id
)
{
return
nodes
[
i
];
}
}
return
nullptr
;
}
static
scclTopoNode_t
*
findNodeById
(
uint64_t
id
,
ByteSpanVector
<
scclTopoNode_t
>&
nodes
)
{
for
(
size_t
i
=
0
;
i
<
nodes
.
size
();
++
i
)
{
if
(
nodes
[
i
]
->
id
==
id
)
{
return
nodes
[
i
];
}
}
return
nullptr
;
}
/**
* @brief 合并两个节点的信息
*
* @param existingNode 指向现有节点的迭代器
* @param newNode 要合并的新节点
*/
static
void
mergeNodes
(
scclTopoNode
*
existingNode
,
const
scclTopoNode
*
newNode
)
{
// 合并busIdStr,保留较小的那个
if
(
newNode
->
busId
<
existingNode
->
busId
)
{
existingNode
->
busId
=
newNode
->
busId
;
}
// 合并neighbors
for
(
size_t
i
=
0
;
i
<
newNode
->
neighborCount
;
++
i
)
{
existingNode
->
addNeighbor
(
newNode
->
neighbors
[
i
]);
}
}
/**
* @brief 检查指定的邻居ID是否存在于邻居数组中
*
* 该函数接收一个邻居数组、邻居计数和一个邻居ID,并检查指定的邻居ID是否存在于邻居数组中。
*
* @param neighbors 邻居数组
* @param neighborCount 邻居计数
* @param neighborId 要检查的邻居ID
* @return 如果找到了指定的邻居ID,则返回`true`;否则返回`false`
*/
static
bool
containsNeighbor
(
const
std
::
array
<
uint64_t
,
topoNodeMaxNeighbors
>&
neighbors
,
size_t
neighborCount
,
uint64_t
neighborId
)
{
return
std
::
find
(
neighbors
.
begin
(),
neighbors
.
begin
()
+
neighborCount
,
neighborId
)
!=
neighbors
.
begin
()
+
neighborCount
;
}
/**
* @brief 根据rccl的xml设置获取调整后的hipDev值
*
* 该函数接收一个hipDev值,并根据rccl的xml设置返回调整后的hipDev值。
*
* @param hipDev 原始hipDev值
* @return 调整后的hipDev值
*/
static
int
getAdjustedGpuHipDev
(
int
hipDev
)
{
static
const
std
::
unordered_map
<
int
,
int
>
hipDevAdjustmentMap
=
{{
1
,
0
},
{
2
,
3
},
{
5
,
4
},
{
6
,
7
}};
auto
it
=
hipDevAdjustmentMap
.
find
(
hipDev
);
if
(
it
!=
hipDevAdjustmentMap
.
end
())
{
return
it
->
second
;
}
return
hipDev
;
}
// TODO: 特殊处理GPU,根据rccl的xml设置numaId,1->0,2->3,5->4,6->7。将来可能有更好的方法
// 定义一个函数,用于获取numa node,并可能修改hipDev的值
/**
* @brief 根据PCI路径和设备类型获取NUMA节点ID字符串
*
* 该函数根据给定的PCI路径和设备类型(GPU或其他),获取对应的NUMA节点ID字符串。
* 如果设备类型是GPU,则首先调整HIP设备号,然后获取调整后的GPU的PCI路径,并从中获取NUMA节点ID字符串。
* 如果设备类型不是GPU,则直接从给定的PCI路径中获取NUMA节点ID字符串。
*
* @param pciPath 指向PCI路径的指针
* @param hipDev 指向HIP设备号的指针
* @param type 设备类型(GPU或其他)
* @param numaIdStr 用于存储NUMA节点ID字符串的字符数组
*/
static
void
getNumaIdStr
(
const
char
*
pciPath
,
int
hipDev
,
int
type
,
char
*
numaIdStr
)
{
if
(
type
==
GPU
)
{
hipDev
=
getAdjustedGpuHipDev
(
hipDev
);
char
*
dstPciPath
=
getGpuPciPath
(
hipDev
);
scclTopoGetStrFromSys
(
dstPciPath
,
"numa_node"
,
numaIdStr
);
delete
(
dstPciPath
);
}
else
{
scclTopoGetStrFromSys
(
pciPath
,
"numa_node"
,
numaIdStr
);
}
}
/**
* @brief 从指定的PCI路径和节点ID(interRank)生成唯一的设备ID
*
* 该函数通过从指定的PCI路径下的"device"文件中读取设备ID字符串,并将其转换为一个无符号64位整数。
* 然后,函数将节点ID(interRank)编码到生成的ID的前32位,以确保在不同节点上的相同设备具有不同的ID。
* 该函数还接受设备类型(terminalType)和GPU设备标识符(hipDev),并将它们编码到生成的ID的中间12位。
* 设备类型(terminalType)占用中间12位的高4位,GPU设备标识符(hipDev)占用中间12位的低8位。
*
* @param pciPath 指向PCI设备的路径
* @param interRank 节点ID,用于区分不同节点上的相同设备
* @param terminalType 设备类型,例如GPU、NIC等
* @param hipDev GPU/NIC设备的标识符
* @param numaId NUMA节点的标识符
* @return 生成的唯一设备ID
*/
static
uint64_t
getIdFromPciPathWithNuma
(
const
char
*
pciPath
,
int
interRank
,
int
terminalType
,
int
hipDev
,
int
numaId
)
{
uint64_t
id
=
0
;
char
deviceStr
[
9
];
// 假设deviceStr的格式为"xxxx",即4个16进制字符
// 从指定路径下的"device"文件中读取设备ID字符串
scclTopoGetStrFromSys
(
pciPath
,
"device"
,
deviceStr
);
// 将设备ID字符串转换为无符号16位整数
uint16_t
deviceValue
=
static_cast
<
uint16_t
>
(
strtoul
(
deviceStr
,
nullptr
,
16
));
// 组合id
id
=
(
static_cast
<
uint64_t
>
(
interRank
&
0xFFFFFF
)
<<
40
)
|
// 最开始24位为interRank
(
static_cast
<
uint64_t
>
(
hipDev
&
0xFF
)
<<
32
)
|
// 之后8位为hipDev
(
static_cast
<
uint64_t
>
(
deviceValue
&
0xFFFF
)
<<
16
)
|
// 之后16位为deviceValue
(
static_cast
<
uint64_t
>
(
terminalType
&
0xFF
)
<<
8
)
|
// 之后8位表示terminalType
(
static_cast
<
uint64_t
>
(
numaId
&
0xFF
));
// 最后8bit表示numaId
return
id
;
}
// TODO: 各种特殊情况的修改均根据rccl千卡节点xml文件进行修改,后续需要根据具体适配需要再完善
/**
* @brief 从指定的PCI路径和节点ID(interRank)生成唯一的设备ID。
* 不输入numa节点ID的情况下,函数将自动获取当前设备的numa节点ID。
*
* 该函数首先根据PCI路径获取设备类型,然后根据设备类型和hipDev值调整hipDev值。
* 接着,函数从PCI路径中获取NUMA节点ID,并将其与interRank、hipDev值一起传递给getIdFromPciPath函数,生成唯一的设备ID。
*
* @param pciPath 指向PCI设备的路径
* @param interRank 节点ID,用于区分不同节点上的相同设备
* @param hipDev 设备的标识符
* @param numaId 设备的numa_node值
* @return 生成的唯一设备ID
*/
static
uint64_t
getIdFromPciPath
(
const
char
*
pciPath
,
int
interRank
,
int
terminalType
,
int
hipDev
=
-
1
,
int
numaId
=
-
1
)
{
int
type
=
getDeviceTypeFromPciPath
(
pciPath
);
if
(
numaId
<
0
)
{
char
numaIdStr
[
numaIdStrLen
];
getNumaIdStr
(
pciPath
,
hipDev
,
type
,
numaIdStr
);
// 将最大链接带宽字符串转换为长整型数值
numaId
=
strtol
(
numaIdStr
,
nullptr
,
10
);
}
// 对于PCI路径,设置对CPU设备和PCI设备不设置hipDev
if
(
type
==
PCI
&&
terminalType
==
NIC
)
{
hipDev
=
-
1
;
}
uint64_t
id
=
getIdFromPciPathWithNuma
(
pciPath
,
interRank
,
terminalType
,
hipDev
,
numaId
);
return
id
;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
scclTopoNode
::
scclTopoNode
()
:
id
(
0
),
type
(
0
),
busId
(
0
),
speed
(
0
),
width
(
0
),
neighborCount
(
0
)
{}
/**
* @brief 构造函数,用于根据给定的PCI路径和进程间排名创建一个scclTopoNode对象。
*
* 该构造函数首先检查给定的PCI路径是否为一个有效的PCI设备路径。
* 如果是,则从该路径下的相关文件中读取设备的信息,包括设备ID、设备类型、总线ID字符串、速度和带宽。
* 然后,根据读取到的信息初始化scclTopoNode对象的成员变量。
*
* @param pciPath 指向PCI路径的字符指针。
* @param interRank 进程间排名,用于区分不同的节点。
* @param terminalType 终端设备类型,例如GPU、NIC等。
* @param hipDev HIP设备号,用于标识GPU设备或NIC设备。
* @param numaId NUMA节点ID,用于标识设备所属的NUMA节点。
*/
scclTopoNode
::
scclTopoNode
(
const
char
*
pciPath
,
int
interRank
,
int
terminalType
,
int
hipDev
,
int
numaId
)
:
id
(
0
),
type
(
0
),
busId
(
0
),
speed
(
0
),
width
(
0
),
neighborCount
(
0
)
{
// 检查type和device文件是否存在
// 如果不存在,则说明该路径不是一个有效的PCI设备路径,直接返回
if
(
!
isPciDevice
(
std
::
string
(
pciPath
)))
{
return
;
}
//// 1.获取type
this
->
type
=
getDeviceTypeFromPciPath
(
pciPath
);
//// 2.获取busId
// 从PCI路径字符串中获取最后一个非空子字符串,作为总线ID字符串
std
::
string
lastBusIdStr
=
getLastNonEmptySegment
(
pciPath
,
'/'
);
busIdToInt64
(
lastBusIdStr
.
c_str
(),
&
(
this
->
busId
));
//// 3.获取numa node和id
// 从指定路径下的"device"文件中读取设备ID字符串
this
->
id
=
getIdFromPciPath
(
pciPath
,
interRank
,
terminalType
,
hipDev
,
numaId
);
#if 0
int tmp_interRank, tmp_deviceValue, tmp_terminalType, temp_hipDev, tmp_numaId;
getIdComponents(this->id, &tmp_interRank, &tmp_deviceValue, &tmp_terminalType, &temp_hipDev, &tmp_numaId);
printf("new node id:%lu, (InterRank:%d, T:%d, H:%d, N:%d, busIdStr:%s), decompose: (InterRank:%d, V:%d, T:%d, H:%d, N:%d), hipDev=%d\n",
this->id,
interRank,
terminalType,
this->hipDev,
numaId,
lastBusIdStr.c_str(),
tmp_interRank,
tmp_deviceValue,
tmp_terminalType,
temp_hipDev,
tmp_numaId,
hipDev);
#endif
//// 4.获取速度
// 从指定路径下的"max_link_speed"文件中读取最大链接速度字符串
char
speedStr
[
speedStrLen
];
scclTopoGetStrFromSys
(
pciPath
,
"max_link_speed"
,
speedStr
);
// 在pciSpeedMap映射表中查找与最大链接速度字符串最接近的匹配项
std
::
string
speed_map_key
=
findClosestMatch
(
std
::
string
(
speedStr
),
pciSpeedMap
);
// 根据查找到的匹配项,获取速度
this
->
speed
=
pciSpeedMap
[
speed_map_key
];
//// 5.获取带宽
char
widthStr
[
widthStrLen
];
// 从指定路径下的"max_link_width"文件中读取最大链接带宽字符串
scclTopoGetStrFromSys
(
pciPath
,
"max_link_width"
,
widthStr
);
// 将最大链接带宽字符串转换为长整型数值
this
->
width
=
strtol
(
widthStr
,
nullptr
,
10
);
}
/**
* @brief 构造函数,用于根据给定的NUMA ID和进程间排名创建一个表示CPU的scclTopoNode对象。
*
* 该构造函数首先调用generate_topo_node_numa_info函数生成与给定NUMA ID对应的CPU亲和力字符串。
* 如果生成的CPU亲和力字符串不为空,则将其设置为当前对象的cpuAffinity成员变量,
* 并根据给定的进程间排名和NUMA ID设置当前对象的id成员变量。
*
* @param numaId NUMA ID,用于标识不同的NUMA节点。
* @param interRank 进程间排名,用于区分不同的节点。
*/
scclTopoNode
::
scclTopoNode
(
int
numaId
,
int
interRank
)
:
id
(
0
),
type
(
0
),
busId
(
0
),
speed
(
0
),
width
(
0
),
neighborCount
(
0
)
{
// 设置cpu的affinity
auto
cpuAffinityStr
=
generate_topo_node_numa_info
(
numaId
);
if
(
!
cpuAffinityStr
.
empty
())
{
this
->
id
=
(
static_cast
<
uint64_t
>
(
interRank
)
<<
32
)
|
// 前32位为interRank
(
static_cast
<
uint64_t
>
(
numaId
));
// 后32位的后16位为deviceValue
this
->
type
=
CPU
;
}
}
/**
* @brief 向当前节点添加一个邻居节点。
*
* 该函数将给定的邻居节点ID添加到当前节点的neighbors数组中,并增加neighborCount计数器。
* 如果neighbors数组已满,则不会添加新的邻居节点。
*
* @param neighborId 要添加的邻居节点的ID。
*/
void
scclTopoNode
::
addNeighbor
(
uint64_t
neighborId
)
{
if
(
!
containsNeighbor
(
neighbors
,
neighborCount
,
neighborId
)
&&
neighborCount
<
neighbors
.
size
())
{
neighbors
[
neighborCount
++
]
=
neighborId
;
}
}
/**
* @brief 合并两个具有相同id、type和busId的节点,并将它们的neighbors进行合并
*
* 该函数接收一个指向另一个节点的指针,并检查当前节点和该节点的id、type和busId是否相等。
* 如果它们相等,则表示这两个节点是相同的节点,我们可以将它们的neighbors进行合并。
* 合并的过程是将另一个节点的neighbors数组中的每个邻居节点添加到当前节点的neighbors数组中
* (如果当前节点的neighbors数组中尚未包含该邻居节点)。
*
* @param other_node 指向另一个节点的指针
*/
scclResult_t
scclTopoNode
::
combineNode
(
struct
scclTopoNode
*
other_node
)
{
// 检查两个节点的id、type和busId是否相等
if
(
this
->
id
==
other_node
->
id
&&
this
->
type
==
other_node
->
type
&&
this
->
busId
==
other_node
->
busId
)
{
// 合并neighbors
for
(
size_t
i
=
0
;
i
<
other_node
->
neighborCount
;
++
i
)
{
if
(
!
containsNeighbor
(
this
->
neighbors
,
this
->
neighborCount
,
other_node
->
neighbors
[
i
]))
{
if
(
this
->
neighborCount
<
this
->
neighbors
.
size
())
{
this
->
neighbors
[
this
->
neighborCount
++
]
=
other_node
->
neighbors
[
i
];
}
else
{
// 如果neighbors数组已满,则返回错误代码
return
scclInternalError
;
}
}
}
other_node
->
clearNode
();
// 清空被合并的node
}
return
scclSuccess
;
}
void
scclTopoNode
::
clearNode
()
{
this
->
id
=
0
;
this
->
type
=
0
;
this
->
speed
=
0
;
this
->
width
=
0
;
this
->
neighborCount
=
0
;
this
->
busId
=
0
;
}
/**
* @brief 打印当前节点的信息。
*
* 该函数将当前节点的ID、类型、总线ID字符串、速度、带宽、CPU亲和力和邻居节点数量等信息打印到标准输出。
* 然后,它将当前节点的所有邻居节点的ID逐一打印到标准输出。
*
* @param prefix 打印信息的前缀字符串。
*/
void
scclTopoNode
::
printNodeInfo
(
const
char
*
prefix
,
bool
printNeighbor
)
{
int
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
;
getIdComponents
(
this
->
id
,
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
char
busIdStr
[
busIdStrLen
];
int64ToBusId
(
this
->
busId
,
busIdStr
);
printf
(
"%s: node: (InterRank:%d, V:%d, T:%d, H:%d, N:%d, type=%d, busIdStr=%s, speed=%d, width=%d), neighborCount=%zu
\n
"
,
prefix
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
,
this
->
type
,
busIdStr
,
this
->
speed
,
this
->
width
,
this
->
neighborCount
);
if
(
printNeighbor
)
{
for
(
size_t
i
=
0
;
i
<
neighborCount
;
++
i
)
{
getIdComponents
(
neighbors
[
i
],
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
printf
(
"neighbor[%zu] -- (InterRank:%d, V:%d, T:%d, H:%d, N:%d
\n
"
,
i
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: 当前根据千卡节点的rccl的xml进行特殊处理,不确定是否具有普适性
/**
* @brief 根据给定的PCI路径生成拓扑节点
*
* 该函数通过解析给定的PCI路径,生成相应的拓扑节点,并将它们添加到节点向量中。
* 1.检查路径下存在NUMA节点,首先创建NUMA节点。
* 2.根据rccl的xml进行特殊处理:
* - 对于GPU设备,整条path修改其numaId为映射关系的值,不修改hipDev值
* 3.遍历PCI路径的各个部分,为每个部分创建相应的拓扑节点,并根据需要添加节点间的连接关系。
*
* @param pciPath PCI设备路径
* @param interRank 跨节点的排名
* @param nodes 节点向量,函数将生成的节点添加到此向量中
* @return 如果成功,返回scclSuccess;否则返回相应的错误代码
*/
scclResult_t
generate_topo_nodes
(
const
char
*
pciPath
,
int
interRank
,
int
hipDev
,
ByteSpanVector
<
scclTopoNode_t
>&
nodes
)
{
////---- 1.首先确定传入的pciPath路径下有numa图点,表示存在cpu连接关系,创建numa node ----////
if
(
!
std
::
filesystem
::
exists
(
std
::
string
(
pciPath
)
+
"/numa_node"
))
{
WARN
(
"Cannot find numa_node under pciPath: %s. No cpu connection detected."
,
pciPath
);
return
scclInternalError
;
}
// TODO: 根据rccl的xml文件修改的代码,后续查看有没有更好的方法
int
terminalNumaId
=
-
1
;
int
terminalType
=
getDeviceTypeFromPciPath
(
pciPath
);
{
// 如果node的type类型为GPU,则整条path修改numaId
char
numaIdStr
[
numaIdStrLen
];
getNumaIdStr
(
pciPath
,
hipDev
,
terminalType
,
numaIdStr
);
terminalNumaId
=
static_cast
<
int
>
(
strtoul
(
numaIdStr
,
nullptr
,
10
));
}
// 创建numa图点
scclTopoNode_t
numaNode
(
terminalNumaId
,
interRank
);
if
(
numaNode
.
type
<=
0
)
{
WARN
(
"Cannot find correct numa node:%d from pciPath:%s"
,
pciPath
,
terminalNumaId
);
return
scclInternalError
;
}
// 检查numaNode是否已经存在
auto
numaIt
=
findNodeById
(
numaNode
.
id
,
nodes
);
if
(
numaIt
!=
nullptr
&&
numaIt
->
type
==
numaNode
.
type
)
{
mergeNodes
(
numaIt
,
&
numaNode
);
}
else
{
if
(
!
nodes
.
full
())
{
nodes
.
push_back
(
numaNode
);
numaIt
=
nodes
[
nodes
.
size
()
-
1
];
// 更新numaIt指针
}
else
{
// 处理容量不足的情况
WARN
(
"Cannot add numa node:%d from pciPath:%s, nodes full!"
,
pciPath
,
terminalNumaId
);
return
scclInternalError
;
}
}
////---- 2.遍历pciPath,生成拓扑图点 ----////
std
::
istringstream
gpuPathStream
(
pciPath
);
std
::
string
segment
;
std
::
string
parentPath
=
"/"
;
bool
isFirstValidNode
=
true
;
// 标志变量,用于跟踪是否已经添加了numaNode和第一个有效node之间的连接
while
(
std
::
getline
(
gpuPathStream
,
segment
,
'/'
))
{
if
(
!
segment
.
empty
())
{
std
::
string
currentPath
=
parentPath
+
segment
+
"/"
;
//// 1.创建拓扑图点
scclTopoNode_t
node
(
currentPath
.
c_str
(),
interRank
,
terminalType
,
hipDev
,
terminalNumaId
);
if
(
node
.
id
==
0
)
{
parentPath
=
currentPath
;
continue
;
}
//// 2.检查是否已经存在相同的id
auto
it
=
findNodeById
(
node
.
id
,
nodes
);
if
(
it
!=
nullptr
&&
it
->
type
==
node
.
type
)
{
// 如果存在相同的id,合并节点
mergeNodes
(
it
,
&
node
);
}
else
{
if
(
!
nodes
.
full
())
{
nodes
.
push_back
(
node
);
it
=
nodes
[
nodes
.
size
()
-
1
];
// 更新it指针
}
else
{
// 处理容量不足的情况
WARN
(
"ByteSpanVector capacity exceeded"
);
return
scclInternalError
;
}
}
//// 3.给parent和当前node添加neighbor
// 如果存在device文件,则尝试添加连接关系
if
(
isPciDevice
(
std
::
string
(
parentPath
)))
{
uint64_t
parentId
=
getIdFromPciPath
(
parentPath
.
c_str
(),
interRank
,
terminalType
,
hipDev
,
terminalNumaId
);
// 如果parentId和node.id不相等,则添加双向边。否则不添加边
if
(
parentId
!=
node
.
id
)
{
auto
parentIt
=
findNodeById
(
parentId
,
nodes
);
if
(
parentIt
!=
nullptr
)
{
parentIt
->
addNeighbor
(
node
.
id
);
it
->
addNeighbor
(
parentId
);
// 使用更新后的it指针
}
}
}
//// 4.如果这是第一个有效的node,则添加它和numaNode之间的连接
if
(
isFirstValidNode
)
{
numaIt
->
addNeighbor
(
node
.
id
);
// 使用更新后的numaIt指针
it
->
addNeighbor
(
numaIt
->
id
);
// 使用更新后的it指针
isFirstValidNode
=
false
;
// 设置标志变量为false,表示已经添加了连接
}
parentPath
=
currentPath
;
}
}
return
scclSuccess
;
}
/**
* @brief 获取指定NUMA节点的CPU映射信息
*
* 该函数通过读取Linux系统文件系统中的NUMA节点信息,获取指定NUMA节点的CPU映射(cpumap)。
* 它访问 /sys/devices/system/node/nodeX 目录下的cpumap文件,其中X是NUMA节点ID。
* affinity,例如 "00000000,00000000,ffff0000,00000000"
*
* @param numaId NUMA节点的ID,用于构建系统文件路径
* @return std::string 返回表示CPU映射的字符串,格式为系统提供的cpumap内容
*
* @note 该函数依赖于Linux系统的/sys文件系统结构
* @note 函数内部使用scclTopoGetStrFromSys函数从系统文件中读取信息
*/
std
::
string
generate_topo_node_numa_info
(
int
numaId
)
{
char
cpumaskPath
[]
=
"/sys/devices/system/node/node0000"
;
sprintf
(
cpumaskPath
,
"/sys/devices/system/node/node%d"
,
numaId
);
char
affinityStr
[
36
]
=
{
0
};
scclTopoGetStrFromSys
(
cpumaskPath
,
"cpumap"
,
affinityStr
);
return
std
::
string
(
affinityStr
);
}
// 函数输出id分解后的所有数据。与函数getIdFromPciPathWithNuma相对应
void
getIdComponents
(
uint64_t
idToDecompose
,
int
*
interRank
,
int
*
deviceValue
,
int
*
terminalType
,
int
*
hipDev
,
int
*
numaId
)
{
if
(
interRank
)
{
*
interRank
=
static_cast
<
int
>
((
idToDecompose
>>
40
)
&
0xFFFFFF
);
// 提取interRank
}
if
(
hipDev
)
{
*
hipDev
=
static_cast
<
int
>
((
idToDecompose
>>
32
)
&
0xFF
);
// 提取hipDev
}
if
(
deviceValue
)
{
*
deviceValue
=
static_cast
<
int
>
((
idToDecompose
>>
16
)
&
0xFFFF
);
// 提取deviceValue
}
if
(
terminalType
)
{
*
terminalType
=
static_cast
<
int
>
((
idToDecompose
>>
8
)
&
0xFF
);
// 提取terminalType
}
if
(
numaId
)
{
*
numaId
=
static_cast
<
int
>
(
idToDecompose
&
0xFF
);
// 提取numaId
}
}
/**
* @brief 根据hipDev获取GPU的PCI路径
*
* 该函数接收一个hipDev值,并返回对应的GPU的PCI路径。
*
* @param hipDev hipDev值
* @return 如果成功获取PCI路径,则返回指向PCI路径的指针;否则返回`nullptr`
*/
char
*
getGpuPciPath
(
int
hipDev
)
{
char
busIdStr
[
128
];
// 增加数组的大小以容纳更大的PCI域
(
void
)
hipDeviceGetPCIBusId
(
busIdStr
,
sizeof
(
busIdStr
),
hipDev
);
char
*
gpuPath
=
nullptr
;
if
(
getPciPath
(
busIdStr
,
&
gpuPath
)
!=
0
)
{
return
nullptr
;
// 返回nullptr而不是空字符串
}
char
*
result
=
new
char
[
strlen
(
gpuPath
)
+
1
];
std
::
strcpy
(
result
,
gpuPath
);
return
result
;
}
/**
* @brief 根据scclNet和hipDev获取网络设备的PCI路径
*
* 该函数接收一个scclNet对象和一个hipDev值,并返回对应的网络设备的PCI路径。
*
* @param scclNet 指向scclNet对象的指针
* @param hipDev hipDev值
* @return 如果成功获取PCI路径,则返回指向PCI路径的指针;否则返回`nullptr`
*/
char
*
getNetPciPath
(
scclNet_t
*
scclNet
,
int
hipDev
)
{
net
::
scclNetProperties_t
props
;
if
(
scclNet
->
getProperties
(
hipDev
,
&
props
)
!=
0
)
{
return
nullptr
;
// 返回nullptr而不是空字符串
}
char
*
result
=
new
char
[
strlen
(
props
.
pciPath
)
+
1
];
std
::
strcpy
(
result
,
props
.
pciPath
);
return
result
;
}
/**
* @brief 打印拓扑节点的信息
*
* 该函数接收一个节点向量、一个节点索引和一个前缀字符串,打印指定索引处节点的详细信息,
* 包括节点ID、类型、总线ID字符串、速度、宽度、CPU亲和性以及邻居节点的数量。
* 然后,对于每个邻居节点,函数查找并打印其详细信息。
*
* @param nodes 节点向量,Container可以是ByteSpanVector<scclTopoNode_t>类型,也可以是ByteSpanArray<scclTopoNode_t>类型
* @param nodeIndex 要打印的节点在向量中的索引
* @param prefix 打印信息的前缀字符串
*/
void
printTopoNode
(
ByteSpanArray
<
scclTopoNode_t
>&
nodes
,
int
nodeIndex
,
const
char
*
prefix
)
{
scclTopoNode_t
*
node
=
nodes
[
nodeIndex
];
if
(
node
==
nullptr
||
node
->
type
<=
0
)
return
;
int
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
;
getIdComponents
(
node
->
id
,
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
char
busIdStr
[
busIdStrLen
];
int64ToBusId
(
node
->
busId
,
busIdStr
);
printf
(
"%s: Node:%lu (InterRank:%d, V:%d, T:%d, H:%d, N:%d, Type:%d, BusIdStr: %ld/%s), Speed:%d, Width:%d, Neighbor "
"Count: %zu
\n
"
,
prefix
,
node
->
id
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
,
node
->
type
,
node
->
busId
,
busIdStr
,
node
->
speed
,
node
->
width
,
node
->
neighborCount
);
for
(
size_t
i
=
0
;
i
<
node
->
neighborCount
;
++
i
)
{
const
scclTopoNode_t
*
neighborNode
=
findNodeById
(
node
->
neighbors
[
i
],
nodes
);
if
(
neighborNode
!=
nullptr
)
{
getIdComponents
(
neighborNode
->
id
,
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
int64ToBusId
(
neighborNode
->
busId
,
busIdStr
);
printf
(
"- Node ID: %lu, Neighbor[%zu]:%lu (InterRank:%d, V:%d, T:%d, H:%d, N:%d, Type:%d, BusIdStr:%ld/%s, Speed:%d, Width: %d)
\n
"
,
node
->
id
,
i
,
neighborNode
->
id
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
,
neighborNode
->
type
,
neighborNode
->
busId
,
busIdStr
,
neighborNode
->
speed
,
neighborNode
->
width
);
}
}
}
}
// namespace physical_links
}
// namespace bootstrap
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
\ No newline at end of file
src/hardware/topology/bootstrap/physical_links.h
0 → 100644
View file @
571a75b5
#pragma once
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <string>
#include <array>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <filesystem> // 需要C++17支持
#include "container.h"
#include "bootstrap_utils.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
bootstrap
{
constexpr
size_t
topoNodeMaxLocalNodes
=
128
;
// 每个节点最多的node数量
constexpr
size_t
topoNodeMaxNeighbors
=
16
;
// 每个node最多neighbor数量
namespace
physical_links
{
/*
用于生成硬件之间的图点连接关系
依赖于读取/sys/devices 文件夹下的文件来获取信息
例如: /sys/devices/pci0000:98/0000:98:00.0 文件夹下包含各种信息
*/
typedef
struct
scclTopoNode
{
// 构造函数声明
scclTopoNode
();
scclTopoNode
(
const
char
*
pciPath
,
int
interRank
,
int
terminalType
,
int
hipDev
=
-
1
,
int
numaId
=
-
1
);
scclTopoNode
(
int
numaId
,
int
interRank
);
// 添加邻近图点
void
addNeighbor
(
uint64_t
neighborId
);
// 设置busId的字符串
void
setBusIdStr
(
const
char
*
busIdStr
);
// 合并两个具有相同id、type和busIdStr的节点
scclResult_t
combineNode
(
struct
scclTopoNode
*
other_node
);
// 清空节点
void
clearNode
();
// 打印图点信息
void
printNodeInfo
(
const
char
*
prefix
,
bool
printNeighbor
=
false
);
public:
uint64_t
id
;
// 图点id标志
int
type
;
// 图点类型,对应 nodeType_t
int64_t
busId
;
// 总线ID字符串 "0000:00:00.0",例如 "0000:98:03.7",通过int64ToBusId和busIdToInt64变化
int
speed
;
// 速度
int
width
;
// 带宽
std
::
array
<
uint64_t
,
topoNodeMaxNeighbors
>
neighbors
;
// 邻居图点
size_t
neighborCount
;
// 邻居图点的数量
}
scclTopoNode_t
;
// 根据给定的PCI路径生成拓扑节点
scclResult_t
generate_topo_nodes
(
const
char
*
pciPath
,
int
interRank
,
int
hipDev
,
ByteSpanVector
<
scclTopoNode_t
>&
nodes_span
);
// 根据numaId获取pci路径
std
::
string
generate_topo_node_numa_info
(
int
numaId
);
// 输出id分解后的所有数据
void
getIdComponents
(
uint64_t
idToDecompose
,
int
*
interRank
=
nullptr
,
int
*
deviceValue
=
nullptr
,
int
*
terminalType
=
nullptr
,
int
*
hipDev
=
nullptr
,
int
*
numaId
=
nullptr
);
// 获取GPU和NIC的dev对应路径
char
*
getGpuPciPath
(
int
hipDev
);
char
*
getNetPciPath
(
scclNet_t
*
scclNet
,
int
hipDev
);
// 打印拓扑节点的信息
void
printTopoNode
(
ByteSpanArray
<
scclTopoNode_t
>&
nodes
,
int
nodeIndex
,
const
char
*
prefix
);
}
// namespace physical_links
}
// namespace bootstrap
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/bootstrap/readme.MD
0 → 100644
View file @
571a75b5
# SCCL 引导模块文档
## 概述
SCCL引导模块负责在分布式系统中跨多个节点初始化通信环境。该模块处理节点的唯一标识、通信的根节点建立以及通信结构的初始化。核心功能封装在
[
bootstrap.h
](
bootstrap.h
)
头文件中,其对应的实现位于
[
bootstrap.cpp
](
bootstrap.cpp
)
。辅助功能由
[
bootstrap_net.cpp
](
bootstrap_net.cpp
)
、
[
bootstrap_utils.cpp
](
bootstrap_utils.cpp
)
和
[
physical_links.cpp
](
physical_links.cpp
)
提供。
## 核心组件
### 1. BootstrapHandle_t 结构体
[
BootstrapHandle_t
](
bootstrap_utils.h#26
)
结构体用于存储节点的唯一识别信息,包括用于套接字通信的魔数和用于网络通信的套接字地址。
```
c++
typedef
struct
BootstrapHandle
{
uint64_t
magic
;
// 随机数,用于套接字通信
scclSocketAddress_t
addr
;
// 地址,用于网络通信
}
BootstrapHandle_t
;
```
### 2. scclRankInfo_t 结构体
`scclRankInfo_t`
结构体存储每个排名的通信节点信息,包括GPU、RDMA(网络)和CPU节点。
```
c++
typedef
struct
scclRankInfo
{
struct
{
scclSocket_t
listen_sock
;
// 监听套接字
}
cpu
;
struct
{
int
dev
;
char
name
[
8
];
char
gcn
[
8
];
int
compCap
;
int64_t
pciBusId
;
char
pciPath
[
128
];
}
gpu
;
struct
{
int
count
;
char
name
[
8
];
char
pciPath
[
128
];
uint64_t
guid
;
uint8_t
ptrSupport
;
int
speed
;
int
port
;
float
latency
;
int
maxComms
;
int
maxRecvs
;
}
net
;
int
rank
;
int
localRank
;
uint64_t
hostHash
;
uint64_t
pidHash
;
}
scclRankInfo_t
;
```
### 3. scclNodeInfo_t 结构体
`scclNodeInfo_t`
结构体用于存储每个排名的拓扑连接信息。
```
c++
typedef
struct
scclNodeInfo
{
scclTopoNode_t
nodes
[
topoNodeMaxLocalNodes
];
}
scclNodeInfo_t
;
```
### 4. BootstrapComm 结构体
`BootstrapComm`
结构体存储引导通信信息,包括网络属性、排名信息及其他相关数据。
```
c++
typedef
struct
BootstrapComm
{
void
init
(
int
rank
,
int
nRanks
,
int
localRank
,
int
nLocalRanks
);
void
destroy
();
public:
scclNet_t
*
scclNet
;
scclRankPhysSet_t
*
rank_phys_set
;
cpu_set_t
cpuAffinity
;
int
rank
;
int
nRanks
;
int
localRank
;
int
nLocalRanks
;
int
interRank
;
int
nInterRanks
;
int
hipDev
;
int
deviceCnt
;
uint64_t
magic
;
}
BootstrapComm_t
;
```
### 5. Bootstrap 类
`Bootstrap`
类封装了引导初始化过程,包括唯一ID生成、根创建和通信初始化。
```
c++
class
Bootstrap
{
public:
Bootstrap
(
const
BootstrapHandle_t
*
,
int
rank
,
int
nRanks
);
~
Bootstrap
();
scclResult_t
init
(
BootstrapComm_t
*
bootstrap_comm
);
scclResult_t
bootstrapAllGather
(
const
void
*
src_data
,
void
*
dst_data
,
int
data_size
);
private:
scclResult_t
bootstrapRootGatherAndBroadcast
(
BootstrapNodeBasic_t
*
send_data_basic
);
scclResult_t
bootstrapCommInitNodeInfo
(
scclNet_t
*
scclNet
,
scclRankInfo_t
*
rank_info
);
scclResult_t
bootstrapCommAllGather
(
scclRankInfo_t
*
rank_info
,
scclNodeInfo_t
*
node_info
,
scclRankPhysSet_t
*
rank_phys_set
);
scclResult_t
bootstrapNodesLink
(
scclNodeInfo_t
*
node_infos
);
private:
int
rank
,
nRanks
;
int
localRank
,
nLocalRanks
;
int
interRank
,
nInterRanks
;
volatile
uint32_t
*
abortFlag
;
const
BootstrapHandle_t
*
root_handle
;
BootstrapNodeBasic_t
*
all_node_basic
;
bool
socketInitDone
;
pthread_mutex_t
bootstrapMutex
;
pthread_cond_t
bootstrapCond
;
scclIpcSocket_t
*
ipcsocket
;
};
```
## 功能描述
### bootstrapGetUniqueId
该函数为引导过程生成一个唯一ID,利用
`BootstrapHandle_t`
结构体,它初始化引导网络并为通信设置根句柄。
```
c++
scclResult_t
bootstrapGetUniqueId
(
BootstrapHandle_t
*
handle
);
```
### bootstrapCreateRoot
负责为引导过程创建根节点。该函数设置根节点的监听套接字并启动引导通信。
```
c++
scclResult_t
bootstrapCreateRoot
(
BootstrapHandle_t
*
handle
);
```
### Bootstrap::init
`Bootstrap`
类的init方法是核心初始化函数。它设置通信环境,收集并广播排名信息,初始化节点信息,并建立节点连接。
```
c++
scclResult_t
Bootstrap
::
init
(
BootstrapComm_t
*
bootstrap_comm
);
```
## 辅助模块
-
[
bootstrap_net.cpp
](
bootstrap_net.cpp
)
处理与引导相关的网络操作,包括节点之间的套接字通信和数据传输。
-
[
bootstrap_utils.cpp
](
bootstrap_utils.cpp
)
为引导过程提供实用函数,例如哈希、随机数据生成和PCI设备ID检索。
-
[
physical_links.cpp
](
physical_links.cpp
)
定义
`scclTopoNode`
结构体及关联函数,用于生成和管理拓扑节点及其连接。
## 结论
SCCL引导模块为在分布式计算环境中初始化和管理通信提供了一个强大的框架。
通过封装网络通信的复杂性、唯一ID生成和拓扑节点管理,它简化了可扩展和高效通信集合的开发。
src/hardware/topology/graph/graph.cpp
0 → 100644
View file @
571a75b5
#include <iostream>
#include "graph.h"
#include "paths.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
Graph
::
Graph
(
int
rank
,
int
nRanks
)
{
// 构造函数的实现
}
Graph
::~
Graph
()
{
// 析构函数的实现
}
scclResult_t
Graph
::
calculateCommunicationPaths
(
const
BootstrapComm_t
*
bootstrap_comm
)
{
// 通信路径计算的实现
std
::
cout
<<
"Calculating communication paths..."
<<
std
::
endl
;
// 具体的实现细节
auto
path_finder
=
PathFinder
(
bootstrap_comm
);
path_finder
.
findGpuPaths
();
return
scclSuccess
;
}
scclResult_t
Graph
::
buildLogicalTopology
()
{
// 逻辑拓扑构建的实现
std
::
cout
<<
"Building logical topology..."
<<
std
::
endl
;
// 具体的实现细节
return
scclSuccess
;
}
scclResult_t
Graph
::
calculateTopoChannels
()
{
// 根据无向图计算topo路径的实现
std
::
cout
<<
"Calculating topo paths based on undirected graph..."
<<
std
::
endl
;
// 具体的实现细节
return
scclSuccess
;
}
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/graph.h
0 → 100644
View file @
571a75b5
#pragma once
#include <vector>
#include "base.h"
#include "graph_utils.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
class
Graph
{
public:
Graph
(
int
rank
,
int
nRanks
);
virtual
~
Graph
();
// 通信路径计算
scclResult_t
calculateCommunicationPaths
(
const
BootstrapComm_t
*
bootstrap_comm
);
// 逻辑拓扑构建
scclResult_t
buildLogicalTopology
();
// 根据无向图计算topo路径
scclResult_t
calculateTopoChannels
();
private:
std
::
vector
<
std
::
vector
<
int
>>
adjacencyMatrix
;
// 使用邻接矩阵表示图
// 你可以根据需要添加更多的私有成员变量和函数
};
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/graph_utils.cpp
0 → 100644
View file @
571a75b5
#include <string.h>
#include "graph_utils.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
///
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/graph_utils.h
0 → 100644
View file @
571a75b5
#pragma once
#include <string.h>
#include "bootstrap.h"
#include "graph_utils.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
typedef
bootstrap
::
physical_links
::
scclTopoNode_t
scclTopoNode_t
;
typedef
bootstrap
::
scclNodeInfo_t
scclNodeInfo_t
;
typedef
bootstrap
::
BootstrapComm_t
BootstrapComm_t
;
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/paths.cpp
0 → 100644
View file @
571a75b5
#include <unordered_set>
#include "paths.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
PathFinder
::
PathFinder
(
const
BootstrapComm_t
*
bootstrap_comm
)
:
rank
(
bootstrap_comm
->
rank
),
nRanks
(
bootstrap_comm
->
nRanks
),
localRank
(
bootstrap_comm
->
localRank
),
nLocalRanks
(
bootstrap_comm
->
nLocalRanks
),
interRank
(
bootstrap_comm
->
interRank
),
nInterRanks
(
bootstrap_comm
->
nInterRanks
),
node_container_
(
bootstrap_comm
->
rank_phys_set
->
node_info_vec
.
data
(),
bootstrap_comm
->
nRanks
*
bootstrap_comm
->
rank_phys_set
->
node_info_total_bytes
)
{
// 初始化NodeContainer对象
printf
(
"get PathFinder, node_container_=%zu
\n
"
,
node_container_
.
size
());
for
(
size_t
i
=
0
;
i
<
node_container_
.
size
();
++
i
)
{
scclTopoNode_t
*
node
=
node_container_
[
i
];
// 检查node是否有效
if
(
node
->
type
>
CPU
)
{
// 将有效的node添加到graph_nodes中,并保存其neighbor的id
graph_nodes_
[
node
->
id
]
=
std
::
vector
<
uint64_t
>
(
node
->
neighbors
.
begin
(),
node
->
neighbors
.
begin
()
+
node
->
neighborCount
);
// 构建id到index的映射
id_to_index_
[
node
->
id
]
=
i
;
}
}
#if 0
if(rank == 0) {
// 遍历id_to_index_进行打印
for(const auto& pair : id_to_index_) {
uint64_t node_id = pair.first;
size_t index = pair.second;
const scclTopoNode_t* node = node_container_[index];
int interRank, deviceValue, terminalType, hipDev, numaId;
bootstrap::physical_links::getIdComponents(node_id, &interRank, &deviceValue, &terminalType, &hipDev, &numaId);
char busIdStr[17];
int64ToBusId(node->busId, busIdStr);
printf("rank=%d, node=(InterRank:%d, V:%d, T:%d, H:%d, N:%d, type:%d, busIdStr:%s), neighbor_count=%zu",
rank,
interRank,
deviceValue,
terminalType,
hipDev,
numaId,
node->type,
busIdStr,
node->neighborCount);
for(int n = 0; n < node->neighborCount; ++n) {
uint64_t neighbor_id = node->neighbors[n];
const scclTopoNode_t* neighbor_node = findNodeById(neighbor_id);
if(neighbor_node) {
bootstrap::physical_links::getIdComponents(neighbor_id, &interRank, &deviceValue, &terminalType, &hipDev, &numaId);
int64ToBusId(neighbor_node->busId, busIdStr);
printf(", neighbor[%d]=(InterRank:%d, V:%d, T:%d, H:%d, N:%d, type:%d, busIdStr:%s)",
n,
interRank,
deviceValue,
terminalType,
hipDev,
numaId,
neighbor_node->type,
busIdStr);
} else {
printf(", neighbor[%d]=unknown", n);
}
}
printf("\n");
}
}
#endif
}
scclResult_t
PathFinder
::
findGpuPaths
()
{
// 查找所有type为GPU的节点,并执行BFS搜索
for
(
const
auto
&
pair
:
id_to_index_
)
{
uint64_t
id
=
pair
.
first
;
size_t
index
=
pair
.
second
;
// 定位到node
scclTopoNode_t
*
node
=
node_container_
[
index
];
int
hipDev
;
bootstrap
::
physical_links
::
getIdComponents
(
node
->
id
,
nullptr
,
nullptr
,
nullptr
,
&
hipDev
,
nullptr
);
if
(
node
->
type
==
GPU
&&
hipDev
<
nLocalRanks
)
{
printf
(
"bfsFindGpuPaths start_node_id=%lu, running
\n
"
,
node
->
id
);
bfsFindGpuPaths
(
node
->
id
);
}
}
if
(
rank
==
1
)
{
printGpuPaths
();
}
return
scclSuccess
;
}
/////////////////////////////////////////////////////////////////////////////////////////////
/**
* @brief 根据节点ID查找节点
*
* 该函数接收一个节点ID,并在`node_container_`中查找具有该ID的节点。如果找到了具有指定ID的节点,则返回指向该节点的指针;否则返回`nullptr`。
*
* @param id 要查找的节点ID
* @return 如果找到了具有指定ID的节点,则返回指向该节点的指针;否则返回`nullptr`
*/
const
scclTopoNode_t
*
PathFinder
::
findNodeById
(
uint64_t
id
)
const
{
// 使用id_to_index_映射查找具有指定id的节点的索引
auto
it
=
id_to_index_
.
find
(
id
);
if
(
it
!=
id_to_index_
.
end
())
{
return
node_container_
[
it
->
second
];
}
return
nullptr
;
// 如果未找到具有指定id的节点,则返回nullptr
}
// 为std::vector<uint64_t>类型定义一个相等比较函数
struct
VectorEqual
{
bool
operator
()(
const
std
::
vector
<
uint64_t
>&
lhs
,
const
std
::
vector
<
uint64_t
>&
rhs
)
const
{
if
(
lhs
.
size
()
!=
rhs
.
size
())
{
return
false
;
}
for
(
size_t
i
=
0
;
i
<
lhs
.
size
();
++
i
)
{
if
(
lhs
[
i
]
!=
rhs
[
i
])
{
return
false
;
}
}
return
true
;
}
};
// 为std::vector<uint64_t>类型定义一个哈希函数
struct
VectorHash
{
size_t
operator
()(
const
std
::
vector
<
uint64_t
>&
vec
)
const
{
size_t
seed
=
vec
.
size
();
for
(
const
auto
&
i
:
vec
)
{
seed
^=
i
+
0x9e3779b9
+
(
seed
<<
6
)
+
(
seed
>>
2
);
}
return
seed
;
}
};
/**
* @brief 使用广度优先搜索(BFS)查找从起始GPU节点到其他GPU节点的所有路径
*
* 1.该函数从指定的起始GPU节点开始,使用广度优先搜索算法查找所有能够到达的GPU节点,并记录从起始节点到每个目标GPU节点的所有路径。
* 每条路径中的所有节点最多使用一次。
*
* 2.该函数还添加了一个限制,以防止在路径中出现`interRank`在变化后又变回来的情况。
* 也就是说,如果路径从`interRank == 0`连接到`interRank == 1`的节点后,则不能再连接回`interRank == 0`。
*
* @param start_node_id 起始GPU节点的ID
*/
#if 1
void
PathFinder
::
bfsFindGpuPaths
(
uint64_t
start_node_id
)
{
// 使用一个队列来存储当前路径
std
::
queue
<
std
::
vector
<
uint64_t
>>
queue
;
// 使用一个unordered_map来存储每个node的最短路径
std
::
unordered_map
<
uint64_t
,
std
::
vector
<
uint64_t
>>
shortest_paths
;
// 将起始节点加入队列
queue
.
push
({
start_node_id
});
shortest_paths
[
start_node_id
]
=
{
start_node_id
};
// 当队列不为空时,继续搜索
while
(
!
queue
.
empty
())
{
// 从队列中取出一个路径
auto
path
=
queue
.
front
();
queue
.
pop
();
// 获取当前路径的最后一个节点的ID
uint64_t
nodeId
=
path
.
back
();
// 根据节点ID查找对应的节点
const
scclTopoNode_t
*
current_node
=
findNodeById
(
nodeId
);
if
(
current_node
==
nullptr
)
{
continue
;
}
// 如果当前节点是GPU节点且不是起始节点,则将当前路径加入结果
if
(
current_node
->
type
==
GPU
&&
nodeId
!=
start_node_id
)
{
int
hipDev
;
bootstrap
::
physical_links
::
getIdComponents
(
current_node
->
id
,
nullptr
,
nullptr
,
nullptr
,
&
hipDev
,
nullptr
);
if
(
hipDev
<
nLocalRanks
)
{
gpu_paths_
[
start_node_id
].
push_back
(
path
);
}
}
else
{
int
nodeInterRank
;
bootstrap
::
physical_links
::
getIdComponents
(
nodeId
,
&
nodeInterRank
);
// 遍历当前节点的所有邻居节点
for
(
uint64_t
neighbor_id
:
graph_nodes_
.
at
(
nodeId
))
{
if
(
findNodeById
(
neighbor_id
)
==
nullptr
)
{
continue
;
}
// 获取邻居节点的interRank
int
neighbor_inter_rank
;
bootstrap
::
physical_links
::
getIdComponents
(
neighbor_id
,
&
neighbor_inter_rank
);
// 检查邻居节点是否已在当前路径中访问过
bool
visited
=
std
::
find
(
path
.
begin
(),
path
.
end
(),
neighbor_id
)
!=
path
.
end
();
// 检查interRank是否已经存在(仅当interRank改变时)
bool
inter_rank_exists
=
false
;
if
(
neighbor_inter_rank
!=
nodeInterRank
)
{
for
(
uint64_t
node_id
:
path
)
{
if
(
node_id
==
neighbor_id
)
{
inter_rank_exists
=
true
;
break
;
}
}
}
// 如果邻居节点未访问过且interRank未存在,则扩展路径
if
(
!
visited
&&
!
inter_rank_exists
)
{
std
::
vector
<
uint64_t
>
new_path
=
path
;
new_path
.
push_back
(
neighbor_id
);
// 如果新路径比已有的最短路径更短,则更新最短路径
if
(
shortest_paths
.
find
(
neighbor_id
)
==
shortest_paths
.
end
()
||
shortest_paths
[
neighbor_id
].
size
()
>
new_path
.
size
())
{
shortest_paths
[
neighbor_id
]
=
new_path
;
queue
.
push
(
new_path
);
}
}
}
}
}
}
#else
void
PathFinder
::
bfsFindGpuPaths
(
uint64_t
start_node_id
)
{
// 使用一个队列来存储当前路径
std
::
queue
<
std
::
vector
<
uint64_t
>>
queue
;
// 将起始节点加入队列
queue
.
push
({
start_node_id
});
// 当队列不为空时,继续搜索
while
(
!
queue
.
empty
())
{
// 从队列中取出一个路径
auto
path
=
queue
.
front
();
queue
.
pop
();
// 获取当前路径的最后一个节点的ID
uint64_t
nodeId
=
path
.
back
();
// 根据节点ID查找对应的节点
const
scclTopoNode_t
*
current_node
=
findNodeById
(
nodeId
);
if
(
current_node
==
nullptr
)
{
continue
;
}
// 如果当前节点是GPU节点且不是起始节点,则将当前路径加入结果
if
(
current_node
->
type
==
GPU
&&
nodeId
!=
start_node_id
)
{
int
hipDev
;
bootstrap
::
physical_links
::
getIdComponents
(
current_node
->
id
,
nullptr
,
nullptr
,
nullptr
,
&
hipDev
,
nullptr
);
if
(
hipDev
<
nLocalRanks
)
{
gpu_paths_
[
start_node_id
].
push_back
(
path
);
}
}
else
{
int
nodeInterRank
;
bootstrap
::
physical_links
::
getIdComponents
(
nodeId
,
&
nodeInterRank
);
// 遍历当前节点的所有邻居节点
for
(
uint64_t
neighbor_id
:
graph_nodes_
.
at
(
nodeId
))
{
if
(
findNodeById
(
nodeId
)
==
nullptr
)
{
continue
;
}
// 获取邻居节点的interRank
int
neighbor_inter_rank
;
bootstrap
::
physical_links
::
getIdComponents
(
neighbor_id
,
&
neighbor_inter_rank
);
// 检查邻居节点是否已在当前路径中访问过
bool
visited
=
std
::
find
(
path
.
begin
(),
path
.
end
(),
neighbor_id
)
!=
path
.
end
();
// 检查interRank是否已经存在(仅当interRank改变时)
bool
inter_rank_exists
=
false
;
if
(
neighbor_inter_rank
!=
(
nodeInterRank
))
{
for
(
uint64_t
node_id
:
path
)
{
if
((
nodeInterRank
)
==
neighbor_inter_rank
)
{
inter_rank_exists
=
true
;
break
;
}
}
}
// 如果邻居节点未访问过且interRank未存在,则扩展路径
if
(
!
visited
&&
!
inter_rank_exists
)
{
std
::
vector
<
uint64_t
>
new_path
=
path
;
new_path
.
push_back
(
neighbor_id
);
queue
.
push
(
new_path
);
}
}
}
}
}
#endif
/**
* @brief 打印GPU路径信息
*
* 该函数用于打印`gpu_paths_`中存储的所有GPU路径信息。对于每一条路径,
* 它会打印路径的长度以及路径中每个节点的详细信息,包括节点的`interRank`、
* `deviceValue`、`terminalType`和`numaId`。
*/
void
PathFinder
::
printGpuPaths
()
{
// 遍历gpu_paths_中的每一对(start_node_id, paths)
for
(
const
auto
&
start_node_pair
:
gpu_paths_
)
{
uint64_t
start_node_id
=
start_node_pair
.
first
;
// 获取起始节点的ID
char
busIdStr
[
17
]
=
""
;
// 用于存储总线ID字符串
// 根据起始节点的ID查找对应的节点对象
const
scclTopoNode_t
*
start_node
=
findNodeById
(
start_node_id
);
// 如果找到了对应的节点对象,则将其总线ID转换为字符串
if
(
start_node
)
{
int64ToBusId
(
start_node
->
busId
,
busIdStr
);
}
else
{
return
;
}
const
auto
&
paths
=
start_node_pair
.
second
;
// 获取与起始节点关联的所有路径
size_t
path_count
=
paths
.
size
();
// 计算路径的数量
int
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
;
// 根据起始节点的ID获取其interRank、deviceValue、terminalType和numaId
bootstrap
::
physical_links
::
getIdComponents
(
start_node_id
,
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
printf
(
"GPU node ID:%lu (InterRank:%d, V:%d, T:%d, H:%d, N:%d) (Path count: %zu)
\n
"
,
start_node_id
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
,
path_count
);
// 遍历与起始节点关联的所有路径
for
(
const
auto
&
path
:
paths
)
{
size_t
path_length
=
path
.
size
();
// 计算路径的长度
// 打印路径的长度
printf
(
"Path (length: %zu): "
,
path_length
);
// 遍历路径中的每个节点
for
(
size_t
i
=
0
;
i
<
path
.
size
();
++
i
)
{
uint64_t
node_id
=
path
[
i
];
// 获取节点的ID
// 使用findNodeById函数查找节点的详细信息
const
scclTopoNode_t
*
node
=
findNodeById
(
node_id
);
if
(
node
)
{
// 根据节点的ID获取其interRank、deviceValue、terminalType和numaId
bootstrap
::
physical_links
::
getIdComponents
(
node
->
id
,
&
interRank
,
&
deviceValue
,
&
terminalType
,
&
hipDev
,
&
numaId
);
// 将节点的总线ID转换为字符串
int64ToBusId
(
node
->
busId
,
busIdStr
);
// 打印节点的信息,包括其interRank、deviceValue、terminalType、numaId、类型和总线ID字符串
printf
(
"ID:%lu (InterRank:%d, V:%d, T:%d, H:%d, N:%d, type:%d, busIdStr:%s)"
,
node
->
id
,
interRank
,
deviceValue
,
terminalType
,
hipDev
,
numaId
,
node
->
type
,
busIdStr
);
}
// 如果当前节点不是路径中的最后一个节点,则打印" -> "以分隔节点
if
(
i
!=
path
.
size
()
-
1
)
{
printf
(
" -> "
);
}
}
// 换行,准备打印下一条路径
printf
(
"
\n
=============================================
\n
"
);
}
}
}
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/paths.h
0 → 100644
View file @
571a75b5
#pragma once
#include <vector>
#include <queue>
#include <unordered_map>
#include <cstring> // 为了使用strlen
#include "base.h"
#include "graph_utils.h"
namespace
sccl
{
namespace
hardware
{
namespace
topology
{
namespace
graph
{
class
PathFinder
{
public:
// 构造函数
PathFinder
(
const
BootstrapComm_t
*
bootstrap_comm
);
// 打印函数
scclResult_t
findGpuPaths
();
// 打印函数
void
printGpuPaths
();
private:
ByteSpanArray
<
scclTopoNode_t
>
node_container_
;
// 使用NodeContainer来存储nodes数据
std
::
unordered_map
<
uint64_t
,
std
::
vector
<
uint64_t
>>
graph_nodes_
;
// 使用无序映射存储图的节点和它们的邻居
std
::
unordered_map
<
uint64_t
,
std
::
vector
<
std
::
vector
<
uint64_t
>>>
gpu_paths_
;
// 使用无序映射存储从每个GPU节点到其他GPU节点的所有路径
// 存储node.id到nodes_span索引的映射
std
::
unordered_map
<
uint64_t
,
size_t
>
id_to_index_
;
// 使用广度优先搜索(BFS)查找从起始GPU节点到其他GPU节点的所有路径
void
bfsFindGpuPaths
(
uint64_t
start_node_id
);
// 根据node.id查找节点的函数
const
scclTopoNode_t
*
findNodeById
(
uint64_t
id
)
const
;
int
rank
=
-
1
;
// 当前节点的全局排名
int
nRanks
=
0
;
// 总的节点数量
int
localRank
=
-
1
;
// 当前节点在本地计算节点中的排名
int
nLocalRanks
=
0
;
// 本地计算节点中的节点总数
int
interRank
=
-
1
;
// 整个节点在全部节点中的位置
int
nInterRanks
=
0
;
// 全局拥有节点的个数
};
}
// namespace graph
}
// namespace topology
}
// namespace hardware
}
// namespace sccl
src/hardware/topology/graph/readme.MD
0 → 100644
View file @
571a75b5
## 用于构建拓扑图
包括3个部分:
-
物理拓扑构建:CPU、GPU、PCIe、NVLink等物理连接
-
通信路径计算:GPU到其他GPU的连接通信路径(生成无向图)
-
逻辑拓扑构建:生成各种形状的逻辑拓扑,如ring、tree、mesh、等
其中,物理拓扑构建表示硬件节点间的连接,已经在bootstrap文件夹中完成
本文件完成通信路径计算和逻辑拓扑构建
按照文件夹内功能分类如下:
hardware_topo:
-
通信路径计算
logical_topo:
-
逻辑拓扑构建
-
根据无向图计算topo路径
src/hardware/topology/topo_utils.cpp
View file @
571a75b5
...
...
@@ -5,11 +5,28 @@ namespace sccl {
namespace
hardware
{
namespace
topology
{
/**
* 将64位整数转换为PCI总线ID字符串
*
* @param id 输入的64位整数,包含PCI总线ID信息
* @param busId 输出缓冲区,用于存储格式化后的PCI总线ID字符串(格式为"域:总线:设备.功能")
* @return 返回scclSuccess表示成功
*/
scclResult_t
int64ToBusId
(
int64_t
id
,
char
*
busId
)
{
sprintf
(
busId
,
"%04lx:%02lx:%02lx.%01lx"
,
(
id
)
>>
20
,
(
id
&
0xff000
)
>>
12
,
(
id
&
0xff0
)
>>
4
,
(
id
&
0xf
));
return
scclSuccess
;
}
/**
* 将总线ID字符串转换为64位整数值
*
* @param busId 输入的总线ID字符串,可能包含分隔符(.或:)
* @param id 输出参数,用于存储转换后的64位整数值
* @return 返回操作结果,scclSuccess表示成功
*
* @note 总线ID字符串中的非十六进制字符(0-9,a-f,A-F)和分隔符将被忽略
* 转换后的值通过strtol以16进制解析获得
*/
scclResult_t
busIdToInt64
(
const
char
*
busId
,
int64_t
*
id
)
{
char
hexStr
[
17
];
// Longest possible int64 hex string + null terminator.
int
hexOffset
=
0
;
...
...
@@ -27,6 +44,40 @@ scclResult_t busIdToInt64(const char* busId, int64_t* id) {
return
scclSuccess
;
}
#define BUSID_SIZE (sizeof("0000:00:00.0"))
#define BUSID_REDUCED_SIZE (sizeof("0000:00"))
static
void
memcpylower
(
char
*
dst
,
const
char
*
src
,
const
size_t
size
)
{
for
(
int
i
=
0
;
i
<
size
;
i
++
)
dst
[
i
]
=
tolower
(
src
[
i
]);
}
/**
* @brief 获取给定PCI设备的路径。
*
* 此函数根据提供的PCI设备的总线ID(busId),生成该设备在系统中的实际路径。
* 它首先构造一个可能的路径字符串,然后使用`realpath`函数将其转换为实际的文件系统路径。
* 如果路径无法解析,函数将返回一个错误代码。
*
* @param busId 一个C字符串,表示PCI设备的总线ID。例如:"0000:00:00.0"。
* @param path 一个指向字符指针的指针,用于存储解析后的实际路径。调用者负责释放此内存。
*
* @return scclResult_t 返回操作结果。如果成功,返回`scclSuccess`;如果失败,返回`scclSystemError`。
*
* @note 此函数内部使用了`realpath`函数,该函数可能因路径不存在或其他系统错误而失败。
* 在这种情况下,函数将输出一个警告信息,并返回错误代码。
*/
scclResult_t
getPciPath
(
const
char
*
busId
,
char
**
path
)
{
char
busPath
[]
=
"/sys/class/pci_bus/0000:00/../../0000:00:00.0"
;
memcpylower
(
busPath
+
sizeof
(
"/sys/class/pci_bus/"
)
-
1
,
busId
,
BUSID_REDUCED_SIZE
-
1
);
memcpylower
(
busPath
+
sizeof
(
"/sys/class/pci_bus/0000:00/../../"
)
-
1
,
busId
,
BUSID_SIZE
-
1
);
*
path
=
realpath
(
busPath
,
NULL
);
if
(
*
path
==
NULL
)
{
WARN
(
"Could not find real path of %s"
,
busPath
);
return
scclSystemError
;
}
return
scclSuccess
;
}
// 定义一个常量,表示最大字符串长度
static
constexpr
int
MAX_STR_LEN
=
255
;
...
...
src/hardware/topology/topo_utils.h
View file @
571a75b5
#pragma once
#include <string.h>
#include <map>
#include <string>
#include <cstddef>
#include "base.h"
#include "net.h"
#include "hardware_utils.h"
...
...
@@ -9,9 +11,18 @@ namespace sccl {
namespace
hardware
{
namespace
topology
{
#define SCCL_TOPO_NODE_TYPES (6) // 硬件node的类型
#define SCCL_TOPO_MAX_NODE_PER_TYPE (4) // 每个硬件node类型中节点的数量,间接表明网络拓扑结构的最大层数
#define SCCL_TOPO_RANK_MAX_LINKS (8) // 每个rank中节点与当前rank中其他节点的链接数量
constexpr
int
SCCL_TOPO_NODE_TYPES
=
6
;
// 硬件node的类型数量
constexpr
int
SCCL_TOPO_MAX_NODE_PER_TYPE
=
4
;
// 每个硬件node类型中节点的数量,间接表明网络拓扑结构的最大层数
constexpr
int
SCCL_TOPO_RANK_MAX_LINKS
=
8
;
// 每个rank中节点与当前rank中其他节点的链接数量
typedef
enum
:
int
{
CPU
=
1
,
// 实际是numa域
PCI
=
2
,
GPU
=
3
,
NVS
=
4
,
// 包括XGMI和NVLink
NIC
=
5
,
NET
=
6
// 主要是RDMA网卡
}
nodeType_t
;
// 定义 topoPathType_t 枚举类型,用于表示不同的路径类型。
enum
topoPathType
{
...
...
@@ -58,6 +69,8 @@ scclResult_t int64ToBusId(int64_t id, char* busId);
// 将总线ID字符串转换为64位整数
scclResult_t
busIdToInt64
(
const
char
*
busId
,
int64_t
*
id
);
scclResult_t
getPciPath
(
const
char
*
busId
,
char
**
path
);
// 将PCI路径转换为64位整数,路径偏移量和最小偏移量作为参数
scclResult_t
pciPathToInt64
(
char
*
path
,
int
offset
,
int
minOffset
,
int64_t
*
id
);
...
...
src/include/base.h
View file @
571a75b5
...
...
@@ -8,6 +8,7 @@
#include "alloc.h"
#include "utils.h"
#include "asm_ops.h"
#include "container.h"
/*
外部环境变量设置:
...
...
src/include/check.h
View file @
571a75b5
...
...
@@ -74,13 +74,13 @@ static const char* scclGetErrorString(scclResult_t code) {
} \
} while(0);
#define SCCLCHECKGOTO(call, RES, label) \
do { \
RES = call; \
if(RES != scclSuccess && RES != scclInProgress) { \
INFO(SCCL_LOG_CODEALL, "%s:%d -> %d", __func__, __
FIL
E__, __
LIN
E__, RES); \
goto label; \
} \
#define SCCLCHECKGOTO(call, RES, label)
\
do {
\
RES = call;
\
if(RES != scclSuccess && RES != scclInProgress) {
\
INFO(SCCL_LOG_CODEALL, "%s:%d
%s
-> %d", __func__, __
LIN
E__, __
FIL
E__, RES); \
goto label;
\
}
\
} while(0);
#define HIPCHECK(cmd) \
...
...
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment