<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>NCCL on Echo的技术博客</title><link>https://cybersecurityerial.github.io/echo_blog/tags/nccl/</link><description>Recent content in NCCL on Echo的技术博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Wed, 24 Jun 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://cybersecurityerial.github.io/echo_blog/tags/nccl/index.xml" rel="self" type="application/rss+xml"/><item><title>LLM System: 训练框架随笔 04 - Megatron 通信器设计：如何防死锁</title><link>https://cybersecurityerial.github.io/echo_blog/posts/llm-system-training-framework-notes-04-megatron-communicator-deadlock/</link><pubDate>Wed, 17 Jun 2026 00:00:00 +0800</pubDate><guid>https://cybersecurityerial.github.io/echo_blog/posts/llm-system-training-framework-notes-04-megatron-communicator-deadlock/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本篇目标：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="为什么通信器会死锁"&gt;为什么通信器会死锁&lt;/h2&gt;
&lt;h2 id="megatron-的-p2p-通信抽象"&gt;Megatron 的 P2P 通信抽象&lt;/h2&gt;
&lt;h2 id="batch-p2p-comm"&gt;batch p2p comm&lt;/h2&gt;
&lt;h2 id="overlap-p2p-comm"&gt;overlap p2p comm&lt;/h2&gt;
&lt;h2 id="warmup--steady--cooldown-里的通信顺序"&gt;warmup / steady / cooldown 里的通信顺序&lt;/h2&gt;
&lt;h2 id="如何设计防死锁的通信接口"&gt;如何设计防死锁的通信接口&lt;/h2&gt;
&lt;h2 id="todo"&gt;TODO&lt;/h2&gt;</description></item><item><title>工程踩坑：用 Ray 分布式启动 NCCL 的巨额冷启动开销问题</title><link>https://cybersecurityerial.github.io/echo_blog/posts/ray-distributed-nccl-cold-start-overhead/</link><pubDate>Wed, 24 Jun 2026 00:00:00 +0800</pubDate><guid>https://cybersecurityerial.github.io/echo_blog/posts/ray-distributed-nccl-cold-start-overhead/</guid><description>&lt;p&gt;单机八卡，Ray启动训练任务发现启动很慢，具体慢在torchdist的barrier()，然后做了几组对比实验。分别是经典torch.run，torch.run(virtual_visible)，Ray。&lt;/p&gt;
&lt;p&gt;发现torchrun是正常速度，但是后两者firstbarrier有几秒的时间。&lt;/p&gt;
&lt;p&gt;Ray为了资源隔离，设计的进程模型是只能看到一张卡的形态。认为是这个设定导致了overhead。&lt;/p&gt;
&lt;p&gt;torchdist的barrier由于在整个训练流程的入口，也会承担建立通信域（NCCL视角是Comm，Torch视角是processGroup）的功能，直观来看就有可能是初始化通信域太满了，这是一个猜想。&lt;/p&gt;
&lt;p&gt;深入到torchdist的代码发现barrier做的事情是dist.all_reduce()，这个里面又做了initProcessGroupNCCL-initNCCLComm-initTransport-enqueueNCCLKernel-AsyncWaitKernelQueue
其中从initNCCLComm开始都是NCCL的内部代码。&lt;/p&gt;
&lt;p&gt;但是这个还是不好拆，因为dist.all_reduce是一个AsyncCallback，所以得进一步再往下拆，是丢任务的过程慢了还是任务本身执行慢了。所以这样测：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;work &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dist&lt;span style="color:#f92672"&gt;.&lt;/span&gt;all_reduce(tensor, group&lt;span style="color:#f92672"&gt;=&lt;/span&gt;group, async_op&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;work&lt;span style="color:#f92672"&gt;.&lt;/span&gt;wait()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;返回work之前的时间是丢任务这个过程链路的时间（也就是enqueue以及之前），wait的时间是collkernel执行时间。最后发现是返回work之前很慢。&lt;/p&gt;
&lt;p&gt;所以深入NCCL建立Comm的过程，问题出在cudaDeviceGetPCIBusId这个函数。&lt;/p&gt;
&lt;p&gt;首先需要了解本文语境中的设备可见性问题。这里的设备可见性分为几层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CUDA runtime可见性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个是进程级别的，由CUDA_VISIBLE_DEVICES这个环境变量决定，同时也决定了runtime接口能不能看到足够多的GPU。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;NCCL peer可见性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个是逻辑级别的，代表的是当前rank需要通信的另一个GPU，不考虑物理上是否可达，只描述NCCL要和谁进行通信。&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;NCCL Topology / NVML可见性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个是设备物理连接级别的，需要收集busId，nvmlDev信息。&lt;/p&gt;
&lt;p&gt;现在NCCL建立Comm的时候用的是runtime接口，而如果我们设置了GPU资源的隔离，runtime这一层就真的感知不到busId，而不会选择去找物理拓扑。NCCL对于peer逻辑上可见但找不到物理busId的情况，设置了一种分支状态叫做invisible P2P，在cuda 10.1之后支持。Ray就是触发了这条比较少触发的路径。&lt;/p&gt;
&lt;p&gt;那么为什么触发了这个路径会变慢呢？因为为了p2p又不能基于runtime接口直接hack到busid，就要做一些跨进程共享机制，也就是NCCL语境下的p2p。&lt;/p&gt;
&lt;p&gt;看p2p.cc可以发现跨进程通信有两类，一类是cuMem，一类是IpcHandle。默认走的是cuMem。二者区别在于cuMem是一种eager模式，而IpcHandle是一种lazy模式，所以cuMem一开始慢，但是后面会更快。解决办法就是关掉cuMem。因为目前场景没有看到显著的IpcHandle拖累的现状，后续有需求再改。&lt;/p&gt;</description></item></channel></rss>