首页 >资讯 > > 正文

NCCL源码解析④:建图过程

来源:OneFlow 2023-07-04 12:21:16

作者|KIDGINBROOK

更新|潘丽晨

原文:https://blog.csdn.net/KIDGIN7439/article/details/127493629


(资料图片仅供参考)

上次分析了NCCL对机器PCI系统进行拓扑分析的过程,产出的结果为xml格式,接下来,NCCL会根据这个xml进图的建立过程以便之后进行路径搜索。

ncclTopoGetSystem的最后会执行ncclTopoGetSystemFromXml将xml格式转成图格式。

ncclResult_t ncclTopoGetSystemFromXml(struct ncclXml* xml, struct ncclTopoSystem** topoSystem) {  NCCLCHECK(ncclCalloc(topoSystem, 1));  struct ncclXmlNode* topNode;  NCCLCHECK(xmlFindTag(xml, "system", &topNode));  for (int s=0; s<topNode->nSubs; s++) {    struct ncclXmlNode* node = topNode->subs[s];    if (strcmp(node->name, "cpu") == 0) NCCLCHECK(ncclTopoAddCpu(node, *topoSystem));  }  NCCLCHECK(ncclTopoAddNvLinks(topNode, *topoSystem, NULL));   NCCLCHECK(ncclTopoConnectCpus(*topoSystem));  NCCLCHECK(ncclTopoSortSystem(*topoSystem));   return ncclSuccess;}

从xml中拿到根节点"system",然后遍历子节点中的"cpu",对每个cpu通过ncclTopoAddCpu进行建图,这里一个cpu其实就是一个numa。

ncclResult_t ncclTopoAddCpu(struct ncclXmlNode* xmlCpu, struct ncclTopoSystem* system) {  int numaId;  NCCLCHECK(xmlGetAttrInt(xmlCpu, "numaid", &numaId));  struct ncclTopoNode* cpu;  NCCLCHECK(ncclTopoCreateNode(system, &cpu, CPU, numaId));  const char* str;  NCCLCHECK(xmlGetAttr(xmlCpu, "affinity", &str));  if (str != NULL) {    NCCLCHECK(ncclStrToCpuset(str, &cpu->cpu.affinity));  }   NCCLCHECK(xmlGetAttrStr(xmlCpu, "arch", &str));  NCCLCHECK(kvConvertToInt(str, &cpu->cpu.arch, kvDictCpuArch));  if (cpu->cpu.arch == NCCL_TOPO_CPU_ARCH_X86) {    NCCLCHECK(xmlGetAttrStr(xmlCpu, "vendor", &str));    NCCLCHECK(kvConvertToInt(str, &cpu->cpu.vendor, kvDictCpuVendor));    if (cpu->cpu.vendor == NCCL_TOPO_CPU_VENDOR_INTEL) {      int familyId, modelId;      NCCLCHECK(xmlGetAttrInt(xmlCpu, "familyid", &familyId));      NCCLCHECK(xmlGetAttrInt(xmlCpu, "modelid", &modelId));      cpu->cpu.model = (familyId == 6 && modelId >= 0x55) ? NCCL_TOPO_CPU_TYPE_SKL : NCCL_TOPO_CPU_INTEL_BDW;    }     }  for (int s=0; s<xmlCpu->nSubs; s++) {    struct ncclXmlNode* node = xmlCpu->subs[s];    if (strcmp(node->name, "pci") == 0) NCCLCHECK(ncclTopoAddPci(node, system, cpu));    if (strcmp(node->name, "nic") == 0) {      struct ncclTopoNode* nic = NULL;      NCCLCHECK(ncclTopoGetNode(system, &nic, NIC, 0));      if (nic == NULL) {        NCCLCHECK(ncclTopoCreateNode(system, &nic, NIC, 0));        NCCLCHECK(ncclTopoConnectNodes(cpu, nic, LINK_PCI, LOC_WIDTH));        NCCLCHECK(ncclTopoConnectNodes(nic, cpu, LINK_PCI, LOC_WIDTH));      }         NCCLCHECK(ncclTopoAddNic(node, system, nic));    }     }  return ncclSuccess;}

接着创建一个cpu node,id为numaid,设置cpu的affinity,即该numa对应的核,设置cpu对应vendor等信息。

然后遍历cpu node的子节点,根据不同的类型执行不同的函数,如果是PCI节点,则执行ncclTopoAddPci。

ncclResult_t ncclTopoAddPci(struct ncclXmlNode* xmlPci, struct ncclTopoSystem* system, struct ncclTopoNode* parent) {  const char* str;   int type;  NCCLCHECK(xmlGetAttrStr(xmlPci, "class", &str));  NCCLCHECK(kvConvertToInt(str, &type, kvDictPciClass));   int64_t busId;  NCCLCHECK(xmlGetAttrStr(xmlPci, "busid", &str));  NCCLCHECK(busIdToInt64(str, &busId));   struct ncclTopoNode* node = NULL;  if (type == GPU) {    struct ncclXmlNode* xmlGpu;    NCCLCHECK(xmlGetSub(xmlPci, "gpu", &xmlGpu));    if (xmlGpu == NULL) return ncclSuccess;    int index;    NCCLCHECK(xmlGetAttrIndex(xmlGpu, "rank", &index));    if (index == -1) return ncclSuccess;    NCCLCHECK(ncclTopoCreateNode(system, &node, type, busId));    NCCLCHECK(ncclTopoAddGpu(xmlGpu, system, node));  }  if (type == NIC) {    struct ncclXmlNode* xmlNic;    NCCLCHECK(xmlGetSub(xmlPci, "nic", &xmlNic));    if (xmlNic == NULL) return ncclSuccess;     // Ignore sub device ID and merge multi-port NICs into one PCI device.    busId &= 0xfffffffffffffff0;    struct ncclTopoNode* nicNode = NULL;    NCCLCHECK(ncclTopoGetNode(system, &nicNode, type, busId));    if (nicNode == NULL) {      NCCLCHECK(ncclTopoCreateNode(system, &nicNode, type, busId));      node = nicNode; // Connect it to parent later on    }    NCCLCHECK(ncclTopoAddNic(xmlNic, system, nicNode));  } else if (type == PCI) {    NCCLCHECK(ncclTopoCreateNode(system, &node, type, busId));    for (int s=0; s<xmlPci->nSubs; s++) {      struct ncclXmlNode* xmlSubPci = xmlPci->subs[s];      NCCLCHECK(ncclTopoAddPci(xmlSubPci, system, node));    }  }   if (node) {    int width, speed;    NCCLCHECK(xmlGetAttrInt(xmlPci, "link_width", &width));    NCCLCHECK(xmlGetAttrStr(xmlPci, "link_speed", &str));     // Manage cases where speed was not indicated in /sys    if (width == 0) width = 16;    NCCLCHECK(kvConvertToInt(str, &speed, kvDictPciGen)); // Values in 100Mbps, per lane (we want GB/s in the end)     NCCLCHECK(ncclTopoConnectNodes(node, parent, LINK_PCI, width*speed/80.0));    NCCLCHECK(ncclTopoConnectNodes(parent, node, LINK_PCI, width*speed/80.0));  }  return ncclSuccess;}

首先获取pci的type和busId, 然后判断type,如果是PCI,那么创建一个PCI node,递归执行ncclTopoAddPci,直到遇到NIC或者GPU xml节点。

如果遇到的是NIC,那么创建NIC节点,然后执行ncclTopoAddNic,这里会在xml nic下遍历xml net,对每个xml net创建net node,id为dev,然后设置speed,port,gdr等属性。

ncclResult_t ncclTopoAddNet(struct ncclXmlNode* xmlNet, struct ncclTopoSystem* system, struct ncclTopoNode* nic) {  int dev;  NCCLCHECK(xmlGetAttrInt(xmlNet, "dev", &dev));   struct ncclTopoNode* net;  NCCLCHECK(ncclTopoCreateNode(system, &net, NET, dev));  const char* str;  NCCLCHECK(xmlGetAttr(xmlNet, "guid", &str));  if (str) sscanf(str, "0x%lx", &net->net.asic);  else net->net.asic = dev;   ncclDebugNoWarn = NCCL_GRAPH;  int mbps;  if (xmlGetAttrInt(xmlNet, "speed", &mbps) != ncclSuccess) mbps = 0;  if (mbps <= 0) mbps = 10000; // Some NICs define speed = -1  net->net.width = mbps / 8000.0;  if (xmlGetAttrInt(xmlNet, "port", &net->net.port) != ncclSuccess) net->net.port = 0;  if (xmlGetAttrInt(xmlNet, "gdr", &net->net.gdrSupport) != ncclSuccess) net->net.gdrSupport = 0;  if (xmlGetAttrInt(xmlNet, "maxconn", &net->net.maxChannels) != ncclSuccess) net->net.maxChannels = MAXCHANNELS;  if (xmlGetAttrInt(xmlNet, "coll", &net->net.collSupport) != ncclSuccess) net->net.collSupport = 0;  ncclDebugNoWarn = 0;   NCCLCHECK(ncclTopoConnectNodes(nic, net, LINK_NET, net->net.width));  NCCLCHECK(ncclTopoConnectNodes(net, nic, LINK_NET, net->net.width));  return ncclSuccess;} ncclResult_t ncclTopoAddNic(struct ncclXmlNode* xmlNic, struct ncclTopoSystem* system, struct ncclTopoNode* nic) {  for (int s=0; s<xmlNic->nSubs; s++) {    struct ncclXmlNode* xmlNet = xmlNic->subs[s];    if (strcmp(xmlNet->name, "net") != 0) continue;    int index;    NCCLCHECK(xmlGetAttrIndex(xmlNet, "dev", &index));    if (index == -1) continue;    NCCLCHECK(ncclTopoAddNet(xmlNet, system, nic));  }  return ncclSuccess;}

然后通过建立net node到nic node的正反向边,设置边的类型,边上累计带宽,并且当前节点的边按照带宽从大到小排序。

ncclResult_t ncclTopoConnectNodes(struct ncclTopoNode* node, struct ncclTopoNode* remNode, int type, float width) {  // Aggregate links into higher width for NVLink  struct ncclTopoLink* link;  for (link = node->links; link->remNode; link++) {    if (link->remNode == remNode && link->type == type) break;  }  if (link->remNode == NULL) node->nlinks++;  link->type = type;  link->remNode = remNode;  link->width += width;   // Sort links in BW descending order  struct ncclTopoLink linkSave;  memcpy(&linkSave, link, sizeof(struct ncclTopoLink));  while (link != node->links) {    if ((link-1)->width >= linkSave.width) break;    memcpy(link, link-1, sizeof(struct ncclTopoLink));    link--;  }  memcpy(link, &linkSave, sizeof(struct ncclTopoLink));  return ncclSuccess;}

到这里就添加完成了NIC,回到ncclTopoAddPci里,如果是gpu的话则创建gpu node,然后设置gpu node的rank,dev,gdr等属性。最后通过ncclTopoConnectNodes建立当前节点到子节点的双向边。

到这里就完成了每个numa节点下的建图,然后开始添加nvlink和QPI以连接,先看下nvlink。

ncclResult_t ncclTopoAddNvLinks(struct ncclXmlNode* node, struct ncclTopoSystem* system, const char* parentBusId) {  if (strcmp(node->name, "nvlink") == 0) {    struct ncclTopoNode* gpu = NULL;    int64_t pBusId;    NCCLCHECK(busIdToInt64(parentBusId, &pBusId));    NCCLCHECK(ncclTopoGetNode(system, &gpu, GPU, pBusId));    if (gpu == NULL) {      WARN("Add NVLink error : could not find GPU %lx\n", pBusId);      return ncclInternalError;    }    int count;    NCCLCHECK(xmlGetAttrInt(node, "count", &count));    const char* targetClass;    NCCLCHECK(xmlGetAttrStr(node, "tclass", &targetClass));    int targetType;    NCCLCHECK(kvConvertToInt(targetClass, &targetType, kvDictPciClass));    struct ncclTopoNode* remote = NULL;    if (targetType == GPU) {      // NVL P2P connection to another GPU      const char* target;      NCCLCHECK(xmlGetAttrStr(node, "target", &target));      int64_t busId;      NCCLCHECK(busIdToInt64(target, &busId));      NCCLCHECK(ncclTopoGetNode(system, &remote, GPU, busId));    } else if (targetType == CPU) {      // NVL connection to the local CPU      NCCLCHECK(findLocalCpu(gpu, &remote));    } else {      if (system->nodes[NVS].count == 0) {        NCCLCHECK(ncclTopoCreateNode(system, &remote, NVS, 0));      } else {        remote = system->nodes[NVS].nodes;      }    }    if (remote) {      int nvlSpeed = gpu->gpu.cudaCompCap == 60 ? PASCAL_NVLINK_WIDTH : VOLTA_NVLINK_WIDTH;      NCCLCHECK(ncclTopoConnectNodes(gpu, remote, LINK_NVL, count*nvlSpeed));      if (remote->type != GPU) {        NCCLCHECK(ncclTopoConnectNodes(remote, gpu, LINK_NVL, count*nvlSpeed));      }    }  } else {    const char* busId;    NCCLCHECK(xmlGetAttr(node, "busid", &busId));    for (int s=0; s<node->nSubs; s++) {      NCCLCHECK(ncclTopoAddNvLinks(node->subs[s], system, busId ? busId : parentBusId));    }  }  return ncclSuccess;}

从根节点递归遍历下去,直到遇到nvlink xml节点,然后拿到nvlink的父节点,即gpu节点,然后通过tclass获取对端PCI设备类型,如果是gpu或者cpu,直接返回对端node,如果是nvswitch,那就先创建nvswitch节点,然后创建当前gpu节点和对端的双向边。然后通过ncclTopoConnectCpus将cpu两两连接。

最后为了方便后续搜索channel,通过ncclTopoSort递归将每个PCI节点的边按照nvlink,向下的PCI连接,向上的PCI连接,QPI的顺序进行排序,因为建边的过程中已经按照带宽排序过,所以nvlink一定在最前边,QPI一定在最后,因此只需要对中间的PCI排序即可。

static ncclResult_t ncclTopoSort(struct ncclTopoNode* node, struct ncclTopoNode* upNode) {  // Shift all links to have upLink as last link  if (upNode) {    int l=0;    while (node->links[l].remNode != upNode) l++;    struct ncclTopoLink upLink;    memcpy(&upLink, node->links+l, sizeof(struct ncclTopoLink));    while (node->links[l+1].remNode) {      memcpy(node->links+l, node->links+l+1, sizeof(struct ncclTopoLink));      l++;    }       memcpy(node->links+l, &upLink, sizeof(struct ncclTopoLink));  }   // Recursively sort the PCI tree  for (int l=0; l<node->nlinks; l++) {    struct ncclTopoLink* link = node->links+l;    if (link->type == LINK_PCI && link->remNode != upNode) NCCLCHECK(ncclTopoSort(link->remNode, node));  }  return ncclSuccess;}

到这里就完成了整个的建图过程。总结下,由于拓扑分析产出的xml不便于进行后续的路径搜索,所以本节基于xml对PCI系统进行了建图。

欢迎 Star、试用 OneFlow 最新版本:
https://github.com/Oneflow-Inc/oneflow/

上一篇:世界快看:今年前5个月国内汽车整车出口193.3万辆 下一篇:最后一页
x
推荐阅读

NCCL源码解析④:建图过程

2023-07-04

世界快看:今年前5个月国内汽车整车出口193.3万辆

2023-07-04

头条焦点:合并没有意义,体面的离开才是雪铁龙和标致最好的结局

2023-07-04

国泰君安晨会纪要|天天快讯

2023-07-04

外媒:欧盟拟让步,俄这家银行有望重返SWIFT系统|世界今亮点

2023-07-04

焦点简讯:英汉词典在线翻译器_英汉字典在线翻译

2023-07-04

做精做好渔业“种子” 天天速递

2023-07-04

世界微资讯!湖北港口集团阳逻港智慧绿色港口建设取得新突破

2023-07-04

全球热议:基金投顾业务要以发展促规范

2023-07-04

焦点速递!泽璟制药(688266)7月3日主力资金净买入2010.00万元

2023-07-04

每日快看:地中海气候图柱状图_地中海气候柱状图

2023-07-04

当前快讯:糯米酒和哪些食物是最佳拍档呢?

2023-07-04

检方支持起诉解民忧,七旬老人获赔10万元医疗费 世界观天下

2023-07-04

环球百事通!7月3日基金净值:银河智联混合A最新净值3.48,涨1.1%

2023-07-04

咸宁市两条高速公路土地专题获自然资源部批准

2023-07-04

乡村爱情每部叫什么名字_乡村爱情七部分别是什么名字

2023-07-03

cad怎么保存pdf格式的文件(Word2007怎么保存为PDF格式) 世界观速讯

2023-07-03

全国主产区累计收购小麦约500亿斤

2023-07-03

清风头条丨武陵区人民法院:党组书记上廉政党课 着力打造廉政法院铁军 播报

2023-07-03

华宝股份:实际控制人朱林瑶被取消指定居所监视居住|实时焦点

2023-07-03

2023年生态文明贵阳国际论坛将举行 论坛突出三个“更”

2023-07-03

房地产开发企业预提费用的企业所得税处理|全球消息

2023-07-03

nba最新转播时间表 NBA正式开放29队都有直播

2023-07-03

观点:为什么梦幻西游进不去_梦幻西游进不去怎么办相关介绍简介

2023-07-03

访企拓岗、精准帮扶……各地各高校推出务实举措 助力毕业生就业

2023-07-03

长江禁渔三年成果:重庆实现渔民从“退得出”到“稳得住”

2023-07-03

2171元起,Xulu 迷你主机推出 15W 处理器 8G+256G存储_环球观焦点

2023-07-03

实时:祝女人永葆青春祝福语_祝女人青春永驻的短句

2023-07-03

为什么大额存单不能买?

2023-07-03

今年前五个月我国交通重大工程项目加快建设

2023-07-03

每日精选:怎么炒花菜才好吃?

2023-07-03

李小玢怎么读_玢怎么读_环球快播

2023-07-03

行业风向标 | “村超”出圈 暑期旅游需求有望向好-当前消息

2023-07-03

速看:上海申花遭逆转,大暴雨成转折,津门虎14轮不败,贝里奇化身伊布

2023-07-03

人大毕业生盗取学校学生信息被刑拘 警方通报 全球球精选

2023-07-03

有研新材董秘回复: 截至2023年6月20日公司股东户数为85951_世界滚动

2023-07-03

天天讯息:500人代理商大会仅156人签约,兰世立挑战传统快消销售模式引发争议

2023-07-03

用60个三角插插花瓶怎么插_用60个三角插插花瓶-天天微头条

2023-07-03

全球短讯!盯盘:98股均线盘中出现黄金交叉

2023-07-03

985高校研究生被强制要求搬离宿舍!|世界动态

2023-07-03

朱老六(831726)7571.05万股限售股将于7月3日解禁上市,占总股本74.12%-观热点

2023-07-03

甘肃省累计建成农村清洁能源示范村127个 每日快看

2023-07-03

什罗普郡房地产市场受到度假出租增加的打击 全球信息

2023-07-03

天天视点!上海一豪宅拍出1.58亿天价,或由得物创始人竞得

2023-07-03

小区凌晨两三点清运垃圾 未央城管:调整至早6点后

2023-07-03

全球热资讯!经开区安置房分配工作稳步推进

2023-07-03

螃蟹不能和什么一起吃论文_螃蟹不能和什么一起吃 环球聚看点

2023-07-03

许嵩到底有多有才 那些年我们追过的许嵩 环球动态

2023-07-02

“执法大队”变“执法犬队”,陕西镇坪县通报:孩子所为 观点

2023-07-02

1至5月全国国有企业营业总收入同比增长6.2%-微速讯

2023-07-02

老凤祥黄金价格今天多少一克(2023年07月02日)

2023-07-02

环球看热讯:资产构成情况及投资关联企业情况怎么填_资产构成情况及投资关联企业情况

2023-07-02

周鸿祎:大模型的趋势是做“小”做“专”,不会一个大模型搞垄断 热讯

2023-07-02

征途怀旧版boss分布坐标_征途怀旧版试炼任务_世界速讯

2023-07-02

周思源 周思源河海大学 环球关注

2023-07-02

7月1日粮价“开门红”,麦价猛涨不止,玉米持续走高,猪价筑底!

2023-07-02

鞍山农行

2023-07-02

当前聚焦:李代沫最近出了一首歌_贪图 李代沫演唱的歌曲相关内容简介介绍

2023-07-02

逆水寒手游穷山海奇遇触发方法攻略

2023-07-02

es350通病(06款es350通病是什么?)

2023-07-02

看热讯:6月30日基金净值:永赢稳健增强债券A最新净值0.9933,涨0.25%

2023-07-02

当前观察:lisa最新发型长发(抛弃齐刘海换空气刘海)

2023-07-02

本人离职申请书

2023-07-02

吃豆腐豆浆等大豆制品的好处可以治疗疾病吗(吃豆腐豆浆等大豆制品的好处) 前沿热点

2023-07-02

每日速讯:江苏徐塘发电有限责任公司 关于江苏徐塘发电有限责任公司介绍

2023-07-01

天天热推荐:湖北襄阳樊城滨江绿色健康跑开跑

2023-07-01

世界今亮点!《三国志幻想大陆》疑云解密次幕答案

2023-07-01

陆军工程大学2023录取分数线 2023军校录取分数线_环球速递

2023-07-01

济宁市国动办到第一书记村开展“庆七一·我来讲党课”暨帮扶共建活动

2023-07-01

北京海淀卫校宿舍照片_北京海淀卫校 环球新视野

2023-07-01

无需申请开户自动送58_无需申请开户自动送_播报

2023-07-01

焦点要闻:2023年全国铁路暑期运输今日启动!

2023-07-01

国信证券:阿洛酮糖,天然健康甜味剂,看好市场需求增长_天天热头条

2023-07-01

全球今亮点!李家超发文庆祝香港回归26周年,感谢太太的陪伴

2023-07-01

环球快资讯丨索辰科技:6月30日融资买入832.31万元,融资融券余额8835.85万元

2023-07-01

世界焦点!「央视快评」在新赶考之路上勇毅前行——热烈庆祝中国共产党成立102周年

2023-07-01

全球看点:鳊鱼、白条、鲢鱼……江鲜要来啦!钱塘江禁渔结束

2023-07-01

环球热讯:煮熟的虾第二次怎么做好吃

2023-07-01

非主流火星文字体照片_非主流火星文签名

2023-07-01

中美开战美国先打哪里_每日时讯

2023-07-01

全球看热讯:新生儿睡觉不踏实怎么办_为什么新生儿睡觉不踏实

2023-07-01

股票行情快报:信濠光电(301051)6月30日主力资金净卖出1426.31万元

2023-07-01

菁怎么读菁是多音字吗_菁怎么读

2023-07-01

罗马诺:登贝莱不会触发解约条款,将留巴萨双方正在谈续长约

2023-06-30

宋华 观察

2023-06-30

热讯:强根劲宝胶囊官网_强根劲宝胶囊

2023-06-30

环球最新:vivo X Flip出厂有贴膜吗

2023-06-30

热消息:隆基6月单晶硅片价格公示 “P型M10 150μm厚度”价格下调32.8%

2023-06-30

39.1℃,北京一地热成“全国第一”,未来十天会热八九天 环球热点

2023-06-30

天籁20周年献礼,高端品质越级实力震撼登场! 天天速看

2023-06-30

欧盟启动联合重整军备计划

2023-06-30

泰嘉股份6月30日快速回调|全球看热讯

2023-06-30

交通运输部副部长披露“鲁篷远渔028”事故救助细节:多次派人登船敲击|天天热消息

2023-06-30

世界百事通!“脊柱侧弯唤醒月”,1分钟,教你自测→

2023-06-30

鼎捷软件6月30日盘中跌幅达5% 快资讯

2023-06-30

同一医院三求助,文案相同

2023-06-30

环球关注:警方通报仁济医院事件:实习生为显信息灵通捏造谣言

2023-06-30

汪世杰

2023-06-30

潍柴动力:融资余额26.53亿元,创近一年新低(06-29)

2023-06-30

信维通信06月29日获深股通增持62.75万股

2023-06-30