MSCCL是微软开源的一个支持自定义集合通信算法的开源集合通信库
GitHub网址:GitHub - microsoft/msccl: Microsoft Collective Communication Library
使用MSCCL需要输入自定义的集合通信算法,自定义的算法需要以MSCCL规定的表示存在XML文件里,也就是MSCCL_XML_FILES环境变量指定的XML文件。
也就是说,如果想自己设置集合通信算法,并在GPUs上运行,总共分为以下几步:
① 克隆MSCCL到本地,然后按照GitHub上给的指南进行编译
② 按照MSCCL规定的格式撰写XML文件
③ 设置MSCCL_XML_FILES环境变量为XML文件所在的路径,并且设置NCCL_ALGO环境变量为MSCCL(也可以不设,只要满足条件,MSCCL会优先使用MSCCL自定义算法)
注意:MSCCL只有在满足以下条件的情况下才会使用自定义算法
if ((mscclAlgo->isValid) && (info->comm->bandwidths[info->coll][NCCL_ALGO_MSCCL][mscclAlgo->protocol] > 0)
&& (mscclAlgo->collectiveType == info->coll) && (inPlace == mscclAlgo->inPlace) && (mscclAlgo->ngpus == info->comm->nRanks)
&& ((totalCount % mscclAlgo->nchunksPerLoop) == 0) && (info->nBytes >= mscclAlgo->minBytes) && (info->nBytes < mscclAlgo->maxBytes)) {
info->algorithm = NCCL_ALGO_MSCCL;
info->protocol = mscclAlgo->protocol;
info->mscclInfo.mscclAlgoIndex = i;
return ncclSuccess;
}
XML文件的格式和各个参数含义如下所示:
<algo name="taccl" nchannels="1" nchunksperloop="4" proto="Simple" coll="allgather" inplace="1" redop="nop" ngpus="4" minBytes="0" maxBytes="1073741824">
//name:算法名称,这个无关紧要
//nchannels:channel的数量,后续可以通过chan来指定该Block属于哪个channel
//nchunksperloop:一次循环可以结束多少个chunk的操作(不仅仅是自己的是单次循环所有的,例如AllGather算法本rank有1个chunk,那么nchunksperloop就是nranks个chunk)
//proto:底层协议,总共有三个协议:Simple,LL,LL128;Simple是一次传输中间缓冲区的一半,相当于每次都传最多的量;LL是一次传输一个小包,延迟比较低,数据传了立马收,不等攒够;LL128是Simple和LL的折中,包没有LL那么小,设置为128字节
//coll:集合通信算法的名称,执行相应名称的集合通信算法时会用MSCCL的这个算法替换
//inplace:设置该算法是否是原地执行,即sendbuffer(inputbuffer)与recvbuffer(outputbuffer)相同,如果上层调用的不是这个算法的原地类型则不执行MSCCL的这个算法(比如上层调用时sendbuff==recvbuff,但是inplace为0则不会调用这个算法,而是会调用NCCL默认算法中的最优算法)
//redop:MSCCL里没说
//ngpus:指定GPU的数量,MSCCL会根据这个参数接收后面的操作,写的是几就接收几个<gpu>
//minBytes:指定使用自定义算法的最小字节数,为了能保证使用自定义算法,建议设置为0,即任何情况下均满足最小字节数的约束
//maxBytes:指定使用自定义算法的最大字节数,不添加此参数的话,MSCCL会设置其为128MB,如果有需求可以设置得非常大(例子中设置为1GB,注意是字节数目的十进制表示)
<gpu id="0" i_chunks="1" o_chunks="4" s_chunks="0">
//id:GPU的rank号
//i_chunks:input的chunk数量
//o_chunks:output的chunk数量
//s_chunks:临时缓冲区的chunk数量,后续MSCCL会根据ScratchChunk的大小来分配相同大小的缓冲区(该临时缓冲区和中间缓冲区不一样,该临时缓冲区叫ScratchBuffer是MSCCL特有的,和NCCL后端使用的中间缓冲区是不同的缓冲区)
<copy i_off="0" o_off="0"/>
//copy:copy操作,意思是把前面的内容存储进后面的存储空间里,比如这一句的操作就是把inputbuffer里面0号chunk复制到outputbuffer的0号chunk所在位置(MSCCL里没有,看上去是这个意思)
<tb id="0" send="-1" recv="1" chan="0">
//tb:一个tb里面的内容表示一个Block的所有操作
//id:该Block的序号,不能重复,比如这句的意思就是该Block为0号Block
//send:发送给几号rank,不发就是-1
//recv:从几号rank接收,不接收就是-1
//chan:该Block属于几号channel,不同channel同一个peer的中间buffer不同,但是同一个channel下同一个peer的中间buffer是同一个
<step s="0" type="r" srcbuf="o" srcoff="1" dstbuf="o" dstoff="1" cnt="1" depid="-1" deps="-1" hasdep="0"/>
//step:Block操作的第几步,同一步只能是一种操作,可以一次可以多次
//type:MSCCL会根据type值不同来调用不同的NCCL后端原语,TACCL里面只有s和r两种;但是实际上还有rcs,rrs,rrc等操作,TACCL的AllReduce比NCCL慢就是因为使用的原语效率不高
//srcbuf:指定数据从本GPU的哪个buffer取,这里写的是o,说明从本GPU的outputbuffer里面取
//srcoff:源偏移量,标识取源buffer的第几个chunk
//desbuf:指定想要传输给对端哪个buffer,这里写的是o,说明想传给outputbuffer
//dstoff:目的偏移量,标识传给目的buffer的第几个chunk所在的位置
//cnt:该操作重复多少次之后进入下一个step
//depid:表示该操作依赖于几号Block的操作
//deps:表示该操作依赖于第几个Step
//hasdep:该步操作的数据是否是别的Block需要的,0是不需要,1是需要;比如0号Block在Step1时recv的chunk是1号Block在Step2时send所需要的,即1号Block依赖于0号Block,那么这个值就应该是1表示需要进行数据存储同步
</tb>//括回
</gpu>//括回
</algo>//括回