HCCL 拓扑图核心概念:Node、Fabric、Edge
一、概述
在 HCCL 虚拟拓扑(NetInstance)中,数据结构采用**图(Graph)**的组织方式,核心概念包括:
| 概念 | 类 | 说明 |
|---|---|---|
| Node(节点) | Graph::nodes | 图中的顶点,代表 Peer(计算节点)或 Fabric(交换机) |
| Fabric(交换机) | NetInstance::Fabric | 网络交换设备的抽象,表示交换机/路由器 |
| Edge / Link(边) | Graph::edges+NetInstance::Link | 图中的边,描述两个节点之间的连接关系 |
| ConnInterface(接口) | NetInstance::ConnInterface | 节点的连接接口,包含地址和端口信息 |
| Path(路径) | NetInstance::Path | 由一条或多条 Link 组成的通信路径 |
二、Graph 类模板
2.1 数据结构
template<typenameNodeType,typenameEdgeType>classGraph{private:// 三层嵌套 Map:srcNodeId → dstNodeId → edges[]std::unordered_map<NodeId,std::unordered_map<NodeId,std::vector<std::shared_ptr<EdgeType>>>>edges;std::unordered_map<NodeId,std::shared_ptr<NodeType>>nodes;};2.2 edges 的三层结构
edges 的数据结构: ┌─────────────────────────────────────────────────────────────────────────┐ │ edges (三层嵌套 Map) │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 第一层 Key: srcNodeId (源节点ID) │ │ │ │ │ ▼ │ │ 第二层 Key: dstNodeId (目标节点ID) │ │ │ │ │ ▼ │ │ 第三层 Value: vector<shared_ptr<EdgeType>> (边数组) │ │ │ │ │ ├── edge1 (Link) │ │ ├── edge2 (Link) │ │ └── edge3 (Link) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ 为什么是 vector 而不是单个 Edge? → 两个节点之间可能存在多条边(不同端口、不同协议、冗余路径)2.3 核心方法
// 添加节点voidAddNode(constNodeId nodeId,std::shared_ptr<NodeType>node){nodes[nodeId]=node;}// 添加边voidAddEdge(constNodeId srcNodeId,constNodeId dstNodeId,std::shared_ptr<EdgeType>edge){edges[srcNodeId][dstNodeId].push_back(edge);}// 遍历某节点的所有出边voidTraverseEdge(constNodeId srcNodeId,std::function<void(std::shared_ptr<EdgeType>)>func)const;// 遍历两个节点之间的所有边voidTraverseEdge(constNodeId srcNodeId,constNodeId dstNodeId,std::function<void(std::shared_ptr<EdgeType>)>func)const;三、NodeId 生成规则
3.1 NodeId 的二进制结构
┌─────────────────────────────────────────────────────────────┐ │ NodeId (64 bit) │ ├─────────────────────────────────────────────────────────────┤ │ bit[63:32] │ bit[31:0] │ │ 类型标识位 │ ID 值 │ │ ───────────────────┼───────────────────── │ │ Peer: 0b0000...0 │ rankId (如 0, 1, 2, ...) │ │ Fabric: 0b0000...1 │ fabricId (如 0, 1, 2, ...) │ └─────────────────────────────────────────────────────────────┘ 示例: Peer NodeId(rankId=5) = 0x00000000_00000005 Fabric NodeId(fabricId=2) = 0x00000001_000000023.2 NodeId 的生成代码
// Peer 节点 ID 生成NodeId NetInstance::Peer::GenerateNodeId(RankId rankId){// 第 32 位为 0 + rankIdreturn(static_cast<u64>(rankId)|static_cast<u64>(0)<<32);}// Fabric 节点 ID 生成NodeId NetInstance::Fabric::GenerateNodeId(FabricId fabricId)const{// 第 32 位为 1 + fabricIdreturn(static_cast<u64>(fabricId)|static_cast<u64>(1)<<32);}四、Fabric(交换机)详解
4.1 什么是 Fabric
Fabric 是**网络交换设备(交换机/路由器)**的抽象,表示集群中用于跨节点通信的网络设备。
物理拓扑示例: ┌──────────────────────────────────────────────┐ │ Fabric (交换机) │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Switch1 │ │ Switch2 │ │ Switch3 │ │ │ └───┬─────┘ └───┬─────┘ └───┬─────┘ │ └───────┼───────────┼───────────┼──────────────┘ │ │ │ ┌────────────┼───────────┼───────────┼────────────┐ │ │ │ │ │ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ │ Peer0 │ │ Peer1 │ │ Peer2 │ │ Peer3 │ │ Peer4 │ │ (Rank0)│ │ (Rank1)│ │ (Rank2)│ │ (Rank3)│ │ (Rank4)│ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘4.2 Fabric 类的定义
// net_instance.hclassFabric:publicNode{public:explicitFabric(FabricId fabricId,PlaneId planeId):Node(NodeType::FABRIC),fabricId_(fabricId),planeId_(planeId){nodeId_=GenerateNodeId(fabricId);// bit[32] = 1}PlaneIdGetPlaneId()const{returnplaneId_;}private:FabricId fabricId_;// Fabric 节点 IDPlaneId planeId_;// Plane ID(网络平面)};4.3 Fabric 的创建过程
// rank_graph_builder.cc: AddFabricInfo()// 1. 从 ranktable 的地址信息中获取 planeId → fabricId 映射std::map<PlaneId,FabricId>planeId2Node=GetFabricsFromAddrInfo(myLevelInfo.rankAddrs);// 2. 遍历每个 Rank,创建 Fabric 节点for(RankId srcRankId:inRanks){for(AddressInfo addrInfo:addrs){FabricId fabId=planeId2Node[addrInfo.planeId];// 3. 如果 Fabric 不存在则创建shared_ptr<NetInstance::Fabric>fabNode;if(fabNodes[fabId]==nullptr){fabNode=make_shared<NetInstance::Fabric>(fabId,addrInfo.planeId);tempNetInsts_[netLayer][netInstId]->AddNode(fabNode);fabNodes[fabId]=fabNode;}// 4. 创建 Peer ↔ Fabric 之间的链路AddPeer2NetLink(netLayer,netInstId,srcRankId,addrInfo,fabNode,links);}}4.4 Fabric vs Peer
| 类型 | 说明 | NodeId bit[32] |
|---|---|---|
| Peer | 计算节点 | 0 |
| Fabric | 交换设备(交换机/路由器) | 1 |
五、Link(边)详解
5.1 Link 的结构
classLink{std::shared_ptr<NetInstance::Node>source_;// 源节点std::shared_ptr<NetInstance::Node>target_;// 目标节点std::shared_ptr<NetInstance::ConnInterface>sourceIface_;// 源端接口std::shared_ptr<NetInstance::ConnInterface>targetIface_;// 目标端接口LinkType type_;// 链路类型std::set<LinkProtocol>linkProtocols_;// 链路协议LinkDirection direction_;// 方向u32 hop_;// 跳数};5.2 Link 的创建
// rank_graph_builder.cc: AddPeer2NetLink()// 1. 获取 Peer 和 Fabric 节点shared_ptr<NetInstance::Peer>peerNode=peers_.at(rankId);// 2. 创建 Peer → Fabric 的 Linkshared_ptr<NetInstance::Link>peer2netLink=make_shared<NetInstance::Link>(peerNode,// source: PeerfabNode,// target: FabricpeerIface,// sourceIface: Peer 的接口nullptr,// targetIface: Fabric 侧为空LinkType::PEER2NET,link->GetLinkProtocols(),LinkDirection::BOTH,// 双向2// hop 数);// 3. 创建 Fabric → Peer 的 Link(反向)shared_ptr<NetInstance::Link>net2peerLink=make_shared<NetInstance::Link>(fabNode,// source: FabricpeerNode,// target: Peernullptr,// sourceIfacepeerIface,// targetIface: Peer 的接口LinkType::PEER2NET,link->GetLinkProtocols(),LinkDirection::BOTH,2);// 4. 添加到 NetInstancetempNetInsts_[netLayer][netInstId]->AddLink(peer2netLink);tempNetInsts_[netLayer][netInstId]->AddLink(net2peerLink);5.3 AddLink 的实现
voidNetInstance::AddLink(constshared_ptr<NetInstance::Link>&link){NodeId srcNodeId=link->GetSourceNode()->GetNodeId();NodeId dstNodeId=link->GetTargetNode()->GetNodeId();// 检查是否已存在boolhasLink=false;vGraph.TraverseEdge(srcNodeId,dstNodeId,[&](shared_ptr<NetInstance::Link>edge){if(*edge==*link){hasLink=true;return;}});if(hasLink){HCCL_WARNING("[NetInstance::AddLink] the fabric group already has the same link.");return;}// 添加到 vGraphvGraph.AddEdge(srcNodeId,dstNodeId,link);}六、Path(路径)详解
6.1 Path 的结构
structPath{std::vector<Link>links;// 组成路径的 Link 列表LinkDirection direction;// 方向};6.2 InnerNetInstance::GetPaths
vector<NetInstance::Path>InnerNetInstance::GetPaths(constRankId srcRankId,constRankId dstRankId)const{vector<NetInstance::Path>paths;// 1. 获取 NodeIdNodeId srcPeerId=peers.at(srcRankId)->GetNodeId();NodeId dstPeerId=peers.at(dstRankId)->GetNodeId();// 2. 获取直连路径:src ↔ dstvGraph.TraverseEdge(srcPeerId,dstPeerId,[&](shared_ptr<NetInstance::Link>edge){NetInstance::Path path;path.links={*edge};path.direction=edge->GetLinkDirection();paths.emplace_back(path);});// 3. 获取通过 Fabric 的路径:src → fabric → dstfor(auto&fabric:fabrics){NodeId fabricId=fabric->GetNodeId();// src → fabric 的链路vector<NetInstance::Link>srcToFabricLinks;vGraph.TraverseEdge(srcPeerId,fabricId,[&](shared_ptr<NetInstance::Link>edge){srcToFabricLinks.push_back(*edge);});// fabric → dst 的链路vector<NetInstance::Link>fabricToDstLinks;vGraph.TraverseEdge(fabricId,dstPeerId,[&](shared_ptr<NetInstance::Link>edge){fabricToDstLinks.push_back(*edge);});if(!srcToFabricLinks.empty()&&!fabricToDstLinks.empty()){for(auto&srcLink:srcToFabricLinks){for(auto&dstLink:fabricToDstLinks){CheckPortGroupSize(netLayer,srcLink,dstLink);NetInstance::Path path;path.links={srcLink,dstLink};paths.emplace_back(path);}}}}returnpaths;}七、拓扑图可视化
7.1 图的节点和边
vGraph 中的节点和边: ┌──────────────────────────────────────────────────────────────┐ │ │ │ ┌─────────────┐ │ │ │ Peer0 │ NodeId = 0x00000000_00000000 │ │ │ (rankId=0) │ bit[32]=0, rankId=0 │ │ └──────┬──────┘ │ │ │ │ │ ├──▶ Link(Peer0→Fabric0, eth0) │ │ │ │ │ ├──▶ Link(Peer0→Fabric0, eth1) │ │ │ │ │ └──▶ Link(Peer0→Peer1) │ │ │ │ ┌─────────────┐ │ │ │ Fabric0 │ NodeId = 0x00000001_00000000 │ │ │ (fabricId=0)│ bit[32]=1, fabricId=0 │ │ └──────┬──────┘ │ │ │ │ │ ├──▶ Link(Fabric0→Peer0, eth0) │ │ │ │ │ └──▶ Link(Fabric0→Peer1, eth0) │ │ │ └──────────────────────────────────────────────────────────────┘7.2 edges 的实际数据示例
edges={// Peer0 → Fabric0 (2 条边,对应 2 个端口)NodeId(Peer0):{NodeId(Fabric0):[Link(src=Peer0,dst=Fabric0,iface=eth0),Link(src=Peer0,dst=Fabric0,iface=eth1)]},// Peer1 → Fabric0 (1 条边)NodeId(Peer1):{NodeId(Fabric0):[Link(src=Peer1,dst=Fabric0,iface=eth0)]},// Fabric0 → Peer0 (2 条边)NodeId(Fabric0):{NodeId(Peer0):[Link(src=Fabric0,dst=Peer0,iface=eth0),Link(src=Fabric0,dst=Peer0,iface=eth1)]},// Fabric0 → Peer1 (1 条边)NodeId(Fabric0):{NodeId(Peer1):[Link(src=Fabric0,dst=Peer1,iface=eth0)]}};八、CheckPortGroupSize 校验
在构建通过 Fabric 的路径时,会校验源 Peer 和目标 Peer 的端口数量:
voidCheckPortGroupSize(u32 netLayer,NetInstance::Link&srcLink,NetInstance::Link&dstLink){autosrcConnIface=srcLink.GetSourceIface();// src → fabric 链路的源接口autotargetConnIface=dstLink.GetTargetIface();// fabric → dst 链路的目标接口autosrcPortGroupSize=srcConnIface->GetPorts().size();// Peer 的端口数autotgtPortGroupSize=targetConnIface->GetPorts().size();// Peer 的端口数if(srcPortGroupSize!=tgtPortGroupSize){THROW<InvalidParamsException>(StringFormat("[GetPaths][CheckPortGroupSize] portGroupSize is not equal => src[%u], target[%u].""LocatedInfo: NetLayer[%u], localRank[%u], rmtRank[%u], localAddr[%s], rmtAddr[%s]",srcPortGroupSize,tgtPortGroupSize,netLayer,localRankId,remoteRankId,localAddr.Describe().c_str(),remoteAddr.Describe().c_str()));}}检查含义:
srcLink:srcPeer → Fabric 的链路,源接口是 srcPeer 的接口dstLink:Fabric → dstPeer 的链路,目标接口是 dstPeer 的接口- 检查两个 Peer 节点的端口数量是否一致(用于 CLOS 拓扑中 src→Fabric→dst 路径的端口绑定校验)
九、总结
| 概念 | 类/结构 | 说明 |
|---|---|---|
| NodeId | uint64_t | 图中节点的唯一标识,Peer 的 bit[32]=0,Fabric 的 bit[32]=1 |
| Node(节点) | Graph::nodes | 图中的顶点,Peer 或 Fabric |
| Peer(计算节点) | NetInstance::Peer | 计算节点,NodeId bit[32]=0 |
| Fabric(交换机) | NetInstance::Fabric | 网络交换设备,NodeId bit[32]=1 |
| Edge / Link(边) | Graph::edges+NetInstance::Link | 描述两个节点之间的连接关系 |
| ConnInterface(接口) | NetInstance::ConnInterface | 节点的连接接口,包含地址和端口 |
| Path(路径) | vector<Link> | 一条通信路径,由一条或多条 Link 组成 |
| vGraph | Graph<Node, Link> | 拓扑图,存储所有节点和边 |
关键关系:
Peer/Rank ───▶ GetNodeId() ───▶ NodeId (bit[32] 区分类型) │ ▼ vGraph 中的顶点 NodeId ───▶ TraverseEdge() ───▶ edge (Link) │ ├── GetSourceNode() → 源 Peer/Fabric ├── GetTargetNode() → 目标 Peer/Fabric ├── GetSourceIface() → 源端接口(端口信息) └── GetTargetIface() → 目标端接口(端口信息) Edge ───▶ 组成 Path ───▶ 用于 GetPaths 查找通信路径一句话总结:
vGraph是一个有向图,nodes存储 Peer 和 Fabric 两种节点,edges存储节点之间的 Link。topo.json决定"哪些节点之间有边",ranktable.json提供边所需的端口和地址信息,两者通过端口交集生成最终的ConnInterface,进而创建Link加入图中。