<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>yitao's Blog</title><link>https://yitaonote.com/</link><description>yitao的学习笔记</description><generator>Hugo 0.145.0 &amp; FixIt v0.3.17-8212d6fd</generator><language>zh-CN</language><lastBuildDate>Sun, 24 Aug 2025 11:36:09 +0800</lastBuildDate><atom:link href="https://yitaonote.com/index.xml" rel="self" type="application/rss+xml"/><item><title>量化</title><link>https://yitaonote.com/2025/a793bc7/</link><pubDate>Thu, 07 Aug 2025 22:17:38 +0800</pubDate><guid>https://yitaonote.com/2025/a793bc7/</guid><category domain="https://yitaonote.com/categories/%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96/">推理优化</category><description>&lt;hr>
&lt;!--more-->
&lt;h2 id="基本概念" class="heading-element">&lt;span>基本概念&lt;/span>
 &lt;a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考链接：&lt;a href="https://zhuanlan.zhihu.com/p/1912965417447686461"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/1912965417447686461&lt;/a>&lt;/p>&lt;/blockquote>
&lt;ul>
&lt;li>
&lt;p>缩放系数：$s= \dfrac{r_{max} - r_{min}}{q_{max} - q_{min}} = \dfrac{\max(|r_{min}|, |r_{max}|)}{2^{N-1}}$&lt;/p>
&lt;/li>
&lt;li>
&lt;p>零点：$z=round(q_{min}-\dfrac{r_{min}}{s})=round(-2^{N-1}-\dfrac{r_{min}}{s})$&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>这里 $r_{min}$ 指的是输入值的最大和最小值。$q_{min}, q_{max}$ 是指量化后的整数范围。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">quantized&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">x_float&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">x_int8&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">zero_point&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">scale&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dequantized&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">x_int8&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">round&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x_float&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="n">scale&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">zero_point&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里有个前置知识就是，在神经网络中一般权重的形状
都是 $W\in R^{O\times I}$，其中 $O$ 表示输出通道(output channel), $I$ 表示输入通道(input channel)，对于矩阵乘法，即 $y = Wx$, $x\in R^{I}, y\in R^{O}$&lt;/p>
&lt;h2 id="数值精度" class="heading-element">&lt;span>数值精度&lt;/span>
 &lt;a href="#%e6%95%b0%e5%80%bc%e7%b2%be%e5%ba%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>常用浮点数精度：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i51ertg19xj31400lst9s.jpg' alt="image">&lt;/p>
&lt;p>FP8 E4M3
FP8 E5M2&lt;/p>
&lt;h2 id="w4a16-在大batch和小batch的场景中起作用的方式" class="heading-element">&lt;span>W4A16 在大batch和小batch的场景中起作用的方式&lt;/span>
 &lt;a href="#w4a16-%e5%9c%a8%e5%a4%a7batch%e5%92%8c%e5%b0%8fbatch%e7%9a%84%e5%9c%ba%e6%99%af%e4%b8%ad%e8%b5%b7%e4%bd%9c%e7%94%a8%e7%9a%84%e6%96%b9%e5%bc%8f" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>&lt;strong>场景一：大 Batch (Large Batch Size)&lt;/strong>&lt;/p>
&lt;p>这种场景常见于离线推理、批处理任务，目标是最大化吞吐量 (Throughput)，即单位时间内处理的样本总数（QPS, Queries Per Second）。&lt;/p>
&lt;p>&lt;strong>核心瓶颈：内存带宽 (Memory-Bound)&lt;/strong>&lt;/p>
&lt;p>当 Batch Size 很大时，输入激活值的矩阵（形状如 [Batch Size, Seq Length, Hidden Dim]）变得非常“厚”。此时，GPU 需要处理的计算量（FLOPs）极大，为了完成这些计算，它必须不断地从速度较慢的显存（VRAM）中把巨大的权重矩阵加载到速度极快的缓存（SRAM）和计算单元中。&lt;/p>
&lt;p>由于权重矩阵非常庞大（例如，Llama 70B 的权重超过 100GB），加载权重所花费的时间远远超过了实际计算的时间。这时，我们就说系统是 “内存带宽受限” (Memory-Bound) 的。&lt;/p>
&lt;p>W4A16 如何起作用：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>极大缓解内存带宽瓶颈：这是 W4A16 在大 Batch 场景下最核心的优势。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>权重以 4-bit 形式存储在显存中，体积是 FP16 的 1/4。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>从显存加载到高速缓存时，数据传输量只有原来的 1/4，加载时间也相应大幅缩短。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>这意味着 GPU 等待数据的时间大大减少，计算单元可以更快地被“喂饱”，从而提升了整体的运行效率。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>反量化开销被摊销 (Amortized Overhead)：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>虽然 4-bit 权重需要一个额外的反量化步骤（W4 -&amp;gt; W16），这个操作本身是有开销的。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>但在大 Batch 场景下，这个开销被摊薄了。你只需要反量化一次权重矩阵，就可以用它来处理 Batch 中成百上千个样本。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>相比于从内存带宽上节省的大量时间，这点反量化的计算开销几乎可以忽略不计。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>结论：&lt;/strong> 在大 Batch 场景下，W4A16 规格能显著提升吞吐量。其主要作用是通过压缩权重来降低对内存带宽的压力，让 GPU 的计算核心能够火力全开。&lt;/p>
&lt;p>&lt;strong>场景二：小 Batch (Small Batch Size, e.g., Batch Size = 1)&lt;/strong>&lt;/p>
&lt;p>这种场景常见于在线服务、实时交互式应用（如聊天机器人），目标是最小化延迟 (Latency)，即处理单个请求所需的时间。&lt;/p>
&lt;p>&lt;strong>核心瓶颈：计算延迟与固定开销 (Compute-Bound / Overhead-Bound)&lt;/strong>&lt;/p>
&lt;p>当 Batch Size 很小时（尤其是 1），输入激活值的矩阵变得非常“薄”。GPU 需要执行的计算量相对较小。此时，加载权重的时间虽然也存在，但它在总耗时中的占比已经不像大 Batch 场景那么极端。&lt;/p>
&lt;p>系统的瓶颈转变为计算单元执行计算本身的速度以及各种固定开销，例如 Kernel Launch（启动计算核心）的延迟、反量化操作的延迟等。这时，系统更偏向于 “计算受限” (Compute-Bound)。&lt;/p>
&lt;p>W4A16 如何起作用：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>内存带宽优势减弱：&lt;/p>
&lt;ul>
&lt;li>虽然加载 W4 权重依然比加载 W16 权重快，但因为总的计算量很小，这部分节省的时间在总延迟中的占比也变小了，优势不再那么明显。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>反量化开销变得显著：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>关键区别在于，反量化的开销无法被摊销。你为单个样本的单次计算，完整地承受了“权重加载 + 权重反量化”的全部开销。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>这个固定的开销在总耗时中占了更高的比例。在某些情况下，反量化带来的额外计算时间，甚至可能超过因权重压缩节省下来的内存加载时间。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>主要收益变为显存占用：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>在小 Batch 场景下，W4A16 带来的最大好处，从“提升性能”转向了“降低显存占用”。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>它使得原本需要大量显存才能装载的大模型（如 70B 模型需要 &amp;gt;140GB FP16 显存），现在可以在显存较小的硬件上（如单张 40GB/80GB 的 A100/H100）运行起来。这是“能不能跑”的问题，而不是“跑多快”的问题。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>结论：&lt;/strong> 在小 Batch 场景下，W4A16 对降低延迟的效果不确定，有时甚至会因为反量化开销而轻微增加延迟。它最主要的作用是大幅降低了模型的显存门槛，让大模型能够在更多硬件上部署。对于追求极致低延迟的场景，有时专门优化的 INT8（W8A8）方案可能表现更佳。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">特性&lt;/th>
 &lt;th style="text-align: center">大 Batch 场景 (高吞吐量)&lt;/th>
 &lt;th style="text-align: center">小 Batch 场景 (低延迟)&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">核心瓶颈&lt;/td>
 &lt;td style="text-align: center">内存带宽 (Memory-Bound)&lt;/td>
 &lt;td style="text-align: center">计算延迟 / 固定开销 (Compute/Overhead-Bound)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">W4 (权重) 的作用&lt;/td>
 &lt;td style="text-align: center">效果极佳。4-bit 权重极大缓解内存带宽瓶颈，因为加载权重的时间是主要耗时。&lt;/td>
 &lt;td style="text-align: center">效果一般。加载权重的时间占比下降，优势减弱。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">A16 (激活) 的作用&lt;/td>
 &lt;td style="text-align: center">保持模型精度，对性能影响不大。&lt;/td>
 &lt;td style="text-align: center">保持模型精度，对性能影响不大。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">反量化开销&lt;/td>
 &lt;td style="text-align: center">可摊销。一次反量化服务于大量数据，开销占比极低。&lt;/td>
 &lt;td style="text-align: center">显著。固定开销在单次计算中占比更高，可能抵消带宽优势。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">主要收益&lt;/td>
 &lt;td style="text-align: center">显著提升吞吐量 (QPS)、降低显存占用。&lt;/td>
 &lt;td style="text-align: center">主要是降低显存占用，对延迟的改善不确定，有时甚至会劣化。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">适用性&lt;/td>
 &lt;td style="text-align: center">非常适合离线推理、批处理任务。&lt;/td>
 &lt;td style="text-align: center">适用于需要部署超大模型但显存有限的场景，但需仔细评估延迟。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="量化粒度---三种形式" class="heading-element">&lt;span>量化粒度 - 三种形式&lt;/span>
 &lt;a href="#%e9%87%8f%e5%8c%96%e7%b2%92%e5%ba%a6---%e4%b8%89%e7%a7%8d%e5%bd%a2%e5%bc%8f" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;ul>
&lt;li>per-tensor&lt;/li>
&lt;/ul>
&lt;p>这是最粗粒度的量化方式。对整个 tensor 计算出一个统一的缩放因子（scale factor），然后将张量中的所有元素都使用这个缩放因子量化到低精度格式，例如 INT8。例如，一个 INT8 的 per-tensor 动态量化器会找到整个张量的最大绝对值，以此计算出一个缩放因子，然后将所有元素缩放到 INT8 的表示范围内 [-127, +127] 并进行取整1 。&lt;/p>
&lt;ul>
&lt;li>per-token&lt;/li>
&lt;/ul>
&lt;p>这种量化方式的粒度比 per-tensor 更细。对于张量的每一个 token（通常指的是矩阵的每一行），都单独计算并应用一个缩放因子。&lt;/p>
&lt;ul>
&lt;li>per-block&lt;/li>
&lt;/ul>
&lt;p>这种量化方式的粒度比 per-token 更细。它将张量在 token 维度上划分为若干个块（block）。对于每一个块内的所有 token（行），计算出一个统一的缩放因子并应用，。&lt;/p>
&lt;ul>
&lt;li>per-channel&lt;/li>
&lt;/ul>
&lt;p>这种量化方式和 per-token 类似，但是量化维度不一样，对于张量的每一个通道（通常指 hidden dim 维度的一列），都单独计算并应用一个缩放因子。&lt;/p>
&lt;h2 id="离群值-outliers" class="heading-element">&lt;span>离群值 (Outliers)&lt;/span>
 &lt;a href="#%e7%a6%bb%e7%be%a4%e5%80%bc-outliers" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>离群值的存在是模型量化时精度下降的主要原因。这是因为量化将模型中的连续数值映射到有限的离散数值范围（例如 INT4 的范围是 [-7, +7]）。如果数据中存在少数数值远超其他数据的离群值，为了表示这些极端的数值，量化的步长（resolution）就需要增大。&lt;/p>
&lt;p>这样做的直接后果是：&lt;/p>
&lt;p>大多数正常的、幅度较小的数值在量化后会变得非常接近甚至等于零。例如，如果一个数值比组内的最大值小很多倍，它可能会被量化为零，导致大量信息的丢失。&lt;/p>
&lt;p>有限的量化比特无法精确表示这些大部分的正常数值，从而降低了整体的量化精度。&lt;/p>
&lt;p>为了解决这个问题，需要采用平滑（smoothing）技术来减小激活或权重中离群值的影响，使得数值的幅度分布更加均匀。量化方法通过观察任务的 outliner 特点，来针对性地设计量化方法。&lt;/p>
&lt;p>比较有代表性的是SmoothQuant ，它观察到在LLM的推理过程中，激活值（activations）中往往比权重值（weights）更容易出现显著的离群值。SmoothQuant 通过一种数学上等价的Per-channel缩放（channel-wise scaling）操作，将模型量化的难度从激活转移到权重。具体来说，它降低了激活中异常大的数值，使得激活值更容易被量化到低比特（例如 INT8），从而在保持模型精度的前提下，实现更高效的量化推理。&lt;/p>
&lt;h2 id="对称--非对称量化区别" class="heading-element">&lt;span>对称 / 非对称量化区别&lt;/span>
 &lt;a href="#%e5%af%b9%e7%a7%b0--%e9%9d%9e%e5%af%b9%e7%a7%b0%e9%87%8f%e5%8c%96%e5%8c%ba%e5%88%ab" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>项目&lt;/th>
 &lt;th>对称量化&lt;/th>
 &lt;th>非对称量化&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>是否有 zero point&lt;/td>
 &lt;td>❌ 固定为 0&lt;/td>
 &lt;td>✅ 存在 zero_point&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>适用范围&lt;/td>
 &lt;td>权重量化（特别是 centered）&lt;/td>
 &lt;td>激活量化（值范围变化大）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>运算简化&lt;/td>
 &lt;td>✅ 快速，适合矩阵乘法&lt;/td>
 &lt;td>❌ 多了减法，加快复杂度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>表现力&lt;/td>
 &lt;td>❌ 表达负偏移有限&lt;/td>
 &lt;td>✅ 支持偏移，精度更高&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>效果稳定性&lt;/td>
 &lt;td>✅ 稳定&lt;/td>
 &lt;td>✅ 更灵活，适应动态变化&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="量化对象" class="heading-element">&lt;span>量化对象&lt;/span>
 &lt;a href="#%e9%87%8f%e5%8c%96%e5%af%b9%e8%b1%a1" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考链接：&lt;a href="https://zhuanlan.zhihu.com/p/1895945361824122028"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/1895945361824122028&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="lineardense-量化" class="heading-element">&lt;span>Linear(Dense) 量化&lt;/span>
 &lt;a href="#lineardense-%e9%87%8f%e5%8c%96" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>指对模型的 Linear 层进行量化，Linear 层主要分布于：&lt;/p>
&lt;ol>
&lt;li>Attention 中的 Q/K/V/O projection&lt;/li>
&lt;li>MLP（FFN) 中的 gate/up/down&lt;/li>
&lt;li>Embedding (&lt;code>[vocab_size, hidden_size]&lt;/code>) 和 LM Head (&lt;code>[hidden_size, vocab_size]&lt;/code>)&lt;/li>
&lt;li>MoE 中的 expert&lt;/li>
&lt;/ol>
&lt;h3 id="attention量化以-sageattention-为例" class="heading-element">&lt;span>Attention量化，以 SageAttention 为例&lt;/span>
 &lt;a href="#attention%e9%87%8f%e5%8c%96%e4%bb%a5-sageattention-%e4%b8%ba%e4%be%8b" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/28866643551"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/28866643551&lt;/a>&lt;/p>&lt;/blockquote>
&lt;p>首先 SageAttention 是基于 FlashAttention2并采用动态量化。&lt;/p>
&lt;p>SageAttention 基于 FlashAttention 的分块方法，对分块应用per-block的方式进行量化。具体来说，在Q，K，P，V的分块上进行 INT8 量化，然后对乘积进行反量化，主要是为了加速 QK^T 和 PV 的矩阵乘法计算。online softmax 保持全精度。&lt;/p>
&lt;h4 id="per-block-量化" class="heading-element">&lt;span>per-block 量化&lt;/span>
 &lt;a href="#per-block-%e9%87%8f%e5%8c%96" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>&amp;ldquo;块&amp;rdquo;（block）是指将输入张量按序列维度划分的连续数据段。每个块包含固定数量的 token，对整个块使用同一个量化缩放因子。用一个具体的例子说明：&lt;/p>
&lt;p>假设我们有一个 Query 张量，形状为 &lt;code>[1, 8, 512, 128]&lt;/code>（&lt;code>batch=1, heads=8, seq_len=512, head_dim=128&lt;/code>），使用 &lt;code>BLKQ=128&lt;/code> 的块大小：&lt;/p>
&lt;ul>
&lt;li>序列长度 512 被划分为 512 ÷ 128 = 4 个块&lt;/li>
&lt;li>第1块：token 0-127&lt;/li>
&lt;li>第2块：token 128-255&lt;/li>
&lt;li>第3块：token 256-383&lt;/li>
&lt;li>第4块：token 384-511&lt;/li>
&lt;/ul>
&lt;p>对于上述例子，Q 的缩放因子张量形状为 &lt;code>[1, 8, 4]&lt;/code>，即每个 head 的每个块都有一个缩放因子。&lt;/p>
&lt;h4 id="smooth-k" class="heading-element">&lt;span>Smooth-K&lt;/span>
 &lt;a href="#smooth-k" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>K表现出明显的 Channel 方向异常值，但是由于矩阵乘法 $QK^T$ 中，量化只能在token维度上进行，因此无法对K应用per-channel量化。&lt;/p>
&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>某个Channel方向异常值，应用per-channel量化，可以把异常值影响范围缩小到该方向上的S和Z&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;p>但是K的通道异常值表现出一个规律，即每个token的key实际上是所有tokens共享的一个大bias，再加上一个小的token-wise的信号。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4pc88urbuj31ak0d6jv4.jpg' alt="image">&lt;/p>
&lt;p>即在最终量化之前，先从全精度 K 中减去平均值。&lt;/p>
&lt;h4 id="quantization-for-q-k-p-v" class="heading-element">&lt;span>Quantization for Q, K, P, V&lt;/span>
 &lt;a href="#quantization-for-q-k-p-v" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;ul>
&lt;li>Q, K 的量化粒度：Q和K的量化粒度可以设置为 per-token，per-block或per-tensor粒度。但是不可以设置为 per-channel，原因上面说了，内轴在相乘时会约掉，没办法进行反量化&lt;/li>
&lt;li>Q, K的数据类型：之所以对Q和K进行 INT8 量化，原因有两个：其一是因为测试了很多模型，对Q，K，P，V使用INT8 量化比 FP8量化具有更高的准确率；其二是因为在许多常用的GPU上进行INT8矩阵乘法比使用FP8快两倍&lt;/li>
&lt;li>P, V的量化粒度：对P进行per-block量化，对V进行per-channel量化，原因有三个：其一是因为对P进行per-channel量化和对V进行per-token量化不可行，因为反量化需要外部轴的scale factor；其二是P的每一行最大值为1，因此可以为一个块分配一个固定的scale factor $s=\frac{1}{127}$；其三是per-channel量化可以解决V的通道方向异常值问题&lt;/li>
&lt;/ul>
&lt;h4 id="fp16-累加" class="heading-element">&lt;span>FP16 累加&lt;/span>
 &lt;a href="#fp16-%e7%b4%af%e5%8a%a0" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>对 P和V进行量化时，在某些模型层使用INT8的准确率会非常低。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4pcztd6z0j30mq0addhj.jpg' alt="image">&lt;/p>
&lt;p>论文中建议在矩阵乘法PV中使用FP16作为数据类型，并且使用FP16累加器&lt;/p>
&lt;p>使用 FP16 累加器的 FP16 矩阵乘法速度比使用 FP32 累加器快 2 倍。此外，使用 FP16 累加器比使用 FP32 累加器可以节省更多的寄存器资源，从而加快计算速度。其次，表 3 表明，对 P, V 使用 FP16 比使用所有其他 8 位数据类型要精确得多。而且，使用 FP16 累加器与使用 FP32 累加器相比不会导致精度损失。&lt;/p>
&lt;h4 id="关于内部轴不可进行反量化的分析" class="heading-element">&lt;span>关于内部轴不可进行反量化的分析&lt;/span>
 &lt;a href="#%e5%85%b3%e4%ba%8e%e5%86%85%e9%83%a8%e8%bd%b4%e4%b8%8d%e5%8f%af%e8%bf%9b%e8%a1%8c%e5%8f%8d%e9%87%8f%e5%8c%96%e7%9a%84%e5%88%86%e6%9e%90" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>矩阵乘法中，对于每个矩阵你只能沿着公共维度进行量化（下图右边）。根据这个简单的原则，Attention 中四个矩阵可以量化的组合如下。注意能做 per-token，就能做 per-block 量化。其中 P 代表 $softmax(QK^T/\sqrt{d})$&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i45oak307uj312m0l8dgo.jpg' alt="image">&lt;/p>
&lt;p>这是因为在进行矩阵乘法 $QK^{T}$ 后，得到的结果矩阵的维度是 N × N（Q 和 K 的维度都是 N × d）。如果我们对 K 进行了per-channel 量化（下图左边，总共 d 个channel，每个 channel 包含 N 个元素），每个通道都有一个独立的scale factor，总共是 d 个 scale factor。在反量化（dequantization）时，我们需要将量化后的结果乘以对应的scale factor，而QK^T 的结果矩阵的维度是 NxN，根本没有 d 的通道维度不直接对应，因此无法使用 K 的通道维度的缩放因子进行正确的反量化。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">&lt;/th>
 &lt;th style="text-align: center">Q&lt;/th>
 &lt;th style="text-align: center">K&lt;/th>
 &lt;th style="text-align: center">P&lt;/th>
 &lt;th style="text-align: center">V&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">per-channel&lt;/td>
 &lt;td style="text-align: center">❌&lt;/td>
 &lt;td style="text-align: center">❌&lt;/td>
 &lt;td style="text-align: center">❌&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">per-token&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">❌&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td style="text-align: center">per-block&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">✅&lt;/td>
 &lt;td style="text-align: center">❌&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="量化类型" class="heading-element">&lt;span>量化类型&lt;/span>
 &lt;a href="#%e9%87%8f%e5%8c%96%e7%b1%bb%e5%9e%8b" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4bjajqt85j30yt0kvad4.jpg' alt="image">&lt;/p>
&lt;p>INT 量化范式：&lt;/p>
&lt;ol>
&lt;li>FP32作为基准，提供了最大的数值范围和零精度损失，但存储开销最大。&lt;/li>
&lt;li>如果用户不太关心效率，那么INT16格式是最佳选择。INT16格式是最精确的，如果是转换FP32，INT16甚至比FP16更精确。&lt;/li>
&lt;li>对于对实时性要求高的服务，建议采用INT8量化方案，可以在保持较高精度的同时获得显著的性能提升。如果你的网络中某些层需要更高的精度，可以使用W8A16来解决这个问题。&lt;/li>
&lt;li>在资源受限但对精度要求相对较低的场景，则可以采用INT4方案以获得最大的资源效益。&lt;/li>
&lt;/ol>
&lt;h3 id="weight-only" class="heading-element">&lt;span>weight-only&lt;/span>
 &lt;a href="#weight-only" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;h4 id="gptq" class="heading-element">&lt;span>GPTQ&lt;/span>
 &lt;a href="#gptq" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;blockquote>
&lt;p>$\text{Frobenius}$ 范数，简称 F-范数，$||\cdot||^{2}_{2}$ 表示对矩阵的 Frobenius 范数的平方（即所有元素平方和）
Frobenius 范数可以用来衡量矩阵整体的大小，比如在误差分析中，可以用来评估两个矩阵之间的差异程度&lt;/p>&lt;/blockquote>
&lt;p>GPTQ是一种训练后权重量化方法，使用基于二阶信息的逐层量化，成功将每个权重量化至 3-4 位，几乎没有精度损失。GPTQ 对某个 block 内的所有参数逐个量化，每个参数量化后，需要适当调整这个 block 内其他未量化的参数，以弥补量化造成的精度损失。 GPTQ 量化需要准备校准数据集。&lt;/p>
&lt;p>OBS/OBQ/GPTQ等一系列工作的核心就是：&lt;/p>
&lt;p>不直接最小化权重误差，而是：$\min_{q(w)}||(q(w)-w)X||^2_{2}$&lt;/p>
&lt;p>给定权重矩阵W，有以下步骤：&lt;/p>
&lt;ol>
&lt;li>收集激活样本（校准数据集）&lt;/li>
&lt;li>计算输入协方差矩阵 $H = X^{T}X$&lt;/li>
&lt;li>逐列量化 W 的每一列&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>每列权重找最优 int4 表示&lt;/li>
&lt;li>误差反馈，用 Hessian 更新下列的残差&lt;/li>
&lt;/ul>
&lt;ol start="4">
&lt;li>保存量化结果，然后在推理阶段使用 int4 ✖️ float16 / int8的高效矩阵乘法&lt;/li>
&lt;/ol>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cllfsriej30zk0mrgoa.jpg' alt="image">&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cllm5vvmj30zk0irac5.jpg' alt="image">&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cllp0h02j30zk0ckwfn.jpg' alt="image">&lt;/p>
&lt;h4 id="awq" class="heading-element">&lt;span>AWQ&lt;/span>
 &lt;a href="#awq" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;blockquote>
&lt;p>参考：&lt;a href="https://www.zhihu.com/search?type=content&amp;amp;q=AWQ%20%E9%87%8F%E5%8C%96"target="_blank" rel="external nofollow noopener noreferrer">https://www.zhihu.com/search?type=content&amp;amp;q=AWQ%20%E9%87%8F%E5%8C%96&lt;/a>&lt;/p>&lt;/blockquote>
&lt;p>AWQ观察到权重通道对性能的重要性各不相同，通过保留1%的显著权重可以大大减少量化误差。基于此观察，AWQ采用了激活感知权重量化来量化LLM，具体会专注于激活值较大的权重通道，并通过每通道缩放实现最佳量化效果。&lt;/p>
&lt;p>&lt;strong>误区：AWQ量化=W4A16量化&lt;/strong>&lt;/p>
&lt;p>AWQ是一种对模型权重进行低比特量化的方法，使用该方法可以将模型权重(Weight)量化为4bit，并在计算激活值(Activation)时反量化为FP16，即W4A16。也可以基于AWQ方法将权重量化为3bit/8bit，并在计算时是使用4bit/8bit/16bit，由此衍生出W4A4、W4A8等一系列方法。
作者在原文中指出，W4A16可以在精度损失较小的情况下，大幅降低内存占用，且提升模型推理速度，是最常用的方法，因此AWQ和W4A16同镜率较高。&lt;/p>
&lt;p>&lt;strong>显著权重（权重并不同等重要，仅有部分显著权重对结果影响较大）&lt;/strong>&lt;/p>
&lt;p>权重矩阵中显著权重位于哪个通道，找到这个通道，将这个通道内的部分保留原来的精度(fp16)，然后其他部分量化为低 bit。&lt;/p>
&lt;p>步骤：在计算时，首先将激活值对每一列求绝对值的平均值，然后把平均值较大的一列对应的通道视作显著通道，保留FP16精度。对其他通道进行低比特量化，如下图：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4clh2pwwnj30wu0b6whh.jpg' alt="image">&lt;/p>
&lt;p>这里详细解释一下“将激活值对每一列求绝对值的平均值，然后把平均值较大的一列对应的通道视作显著通道”&lt;/p>
&lt;p>对于 $y=xW^{T}$，其中 $y\in R^{B\times O}, W\in R^{O\times I}, x\in R^{B\times I}$&lt;/p>
&lt;p>（这里 chatgpt 的解释是，在数学上 $y = Wx$，实现中记为 $y=xW^{T}$）&lt;/p>
&lt;p>这张图中的 $X\in R^{I\times B}, W\in R^{O\times I}$，因此 $y=Wx \in R^{O\times B}$&lt;/p>
&lt;p>所以不管数学上的表达还是实际上的代码实现，y 的输出通道始终由权重的输出通道决定，即 y 的输出通道是否是显著值，就看对应的激活值那一列，所以这里是求的每一列的绝对值的平均值，把平均值较大的一列视为显著通道。&lt;/p>
&lt;p>但是如果这样做，权重矩阵中有的元素需要用 FP16，而其他元素需要用 INT8，不好写 kernel。因此就引入了 Scaling 方法&lt;/p>
&lt;p>&lt;strong>Scaling（量化时对显著权重进行放大可以降低量化误差）&lt;/strong>&lt;/p>
&lt;p>量化误差主要来源于对权重的量化，AWQ的目标是通过缩放显著权重，减少量化误差&lt;/p>
&lt;p>核心思想：对显著权重按比例放大，然后在计算时相应地缩小输入，这样在量化过程中显著权重的相对误差被降低。&lt;/p>
&lt;p>量化函数（这里量化函数不是表示量化后的整数值，而是指 反量化之后的近似权重值，是直接给出最终用于推理的值）：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4clrb0kfvj30eb02e0sn.jpg' alt="image">&lt;/p>
&lt;p>其中 $N$ 是量化后的比特数，$\Delta$ 是量化因子(scaler)，$\Delta= \dfrac{max(|w|)}{2^{N-1}}$&lt;/p>
&lt;ul>
&lt;li>$w'=Round(\frac{w}{\Delta})$ 是量化过程，&lt;/li>
&lt;li>$\Delta\cdot w'$ 是反量化过程&lt;/li>
&lt;li>$w, \Delta, x$ 都是 fp16 格式，不会带来精度损失，精度损失全部来源于 round 函数&lt;/li>
&lt;/ul>
&lt;p>对于权重 $\text{w}$ 中的单个元素 $w$，引入一个缩放因子 $s>1$，量化过程将 $w$ 与该因子相乘，写作：$\text{w}'=Round(\dfrac{ws}{\Delta'})$，相应将反量化过程写作 $\dfrac{\Delta' \cdot w'}{s}$，对 x 进行逆缩放，则：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cltauu51j312504ydfz.jpg' alt="image">&lt;/p>
&lt;p>其原始量化误差为：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cltpuin4j30qh046q31.jpg' alt="image">&lt;/p>
&lt;p>RoundErr：四舍五入误差，为±0.5&lt;/p>
&lt;p>$\Delta$：量化比例因子，决定误差绝对值大小&lt;/p>
&lt;p>缩放后的量化误差：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4cltkxy43j30us050aa8.jpg' alt="image">&lt;/p>
&lt;p>所以误差比值可以描述为 $\dfrac{\Delta'}{\Delta}\cdot \dfrac{1}{s}$，我们认为 $\Delta'\approx \Delta$，加上 $s>1$，所以作者认为量化时对显著权重进行放大，可以降低量化误差&lt;/p>
&lt;blockquote>
&lt;p>从量化函数来看，AWQ 属于对称量化。这里量化因子 q_scale: $\Delta' = \dfrac{max(|w|)}{2^{N-1}}$&lt;/p>&lt;/blockquote>
&lt;p>&lt;strong>自动计算缩放系数&lt;/strong>&lt;/p>
&lt;p>按照上文的分析，我们需要找到权重矩阵每个通道的缩放系数，使得量化误差最小，即最小化公式4：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4clwdh8dcj30fq02xgll.jpg' alt="image">&lt;/p>
&lt;p>按照作者的观点，激活值越大，对应通道越显著，就应该分配更大的缩放系数降低其量化误差。这里为了简单记忆，作者统计了各通道的平均激活值（计算输入矩阵各列绝对值的平均值），并直接将此作为各通道的缩放系数。&lt;/p>
&lt;h4 id="smoothquant" class="heading-element">&lt;span>SmoothQuant&lt;/span>
 &lt;a href="#smoothquant" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>是一个 W8A8 算法，本质还是跟 AWQ 有点像，主要是将 激活量化 的难度转移到权重上，简单来说就是 除以一个值，然后权重乘以一个值：&lt;/p>
$$ Y=(X*\text{diag}(s)^{-1})*(\text{diag}(s)*W) $$&lt;p>也就是对activate (也就是X)进行缩放，并把相反的缩放系数应用到对应的weight(也就是W)上，得到数学上等价的结果。它的核心观察在于：&lt;/p>
&lt;ul>
&lt;li>由于activation outlier的存在，activation的分布非常不规则；&lt;/li>
&lt;li>weight分布均匀
这样，通过上面的操作，试图把 activation 变得更均匀，而把 weight 的均匀分布变得没有那么均匀，也就是把activation 量化de 难度部分平摊到weight 上&lt;/li>
&lt;/ul>
&lt;p>并且该论文主要实现了三种量化方法：per-tensor、per-token、per-channel。同时也进行了static 和 dynamic量化的区分&lt;/p>
&lt;h3 id="weight-activation" class="heading-element">&lt;span>weight-activation&lt;/span>
 &lt;a href="#weight-activation" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul>
&lt;li>LLM.int8() 发现激活中的异常值集中在一小部分通道中。基于这一点，LLM.int8() 根据输入通道内的离群值分布将激活和权重分成两个不同的部分。包含激活值和权重的异常数据的通道以FP16格式存储，其他通道则以INT8格式存储。&lt;/li>
&lt;/ul>
&lt;h3 id="kv-cache" class="heading-element">&lt;span>KV cache&lt;/span>
 &lt;a href="#kv-cache" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>待补充&lt;/p>
&lt;h2 id="动态量化与静态量化" class="heading-element">&lt;span>动态量化与静态量化&lt;/span>
 &lt;a href="#%e5%8a%a8%e6%80%81%e9%87%8f%e5%8c%96%e4%b8%8e%e9%9d%99%e6%80%81%e9%87%8f%e5%8c%96" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;ol>
&lt;li>dynamic：在线运行的时候统计缩放系数&lt;/li>
&lt;/ol>
&lt;p>动态离线量化仅将模型中特定算子的权重从FP32类型映射成 INT8/16 类型，bias和激活函数 在推理过程中动态量化。但是对于不同的输入值来说，其缩放因子是动态计算的（“动态”的由来）。动态量化是几种量化方法中性能最差的。&lt;/p>
&lt;ol start="2">
&lt;li>static：离线使用标定数据计算好缩放系数&lt;/li>
&lt;/ol>
&lt;p>静态离线量化使用少量无标签校准数据，采用 KL 散度等方法计算量化比例因子。静态量化（Static quantization）与动态量化的区别在于其输入的缩放因子计算方法不同，静态量化的模型在使用前有“calibrate”的过程（校准缩放因子）：准备部分输入（对于图像分类模型就是准备一些图片，其他任务类似），使用静态量化后的模型进行预测，在此过程中量化模型的缩放因子会根据输入数据的分布进行调整。一旦校准完成后，权重和输入的缩放因子都固定（“静态”的由来）。静态量化的性能一般比动态量化好，常用于中等模型和大模型。因此实际中基本都是在用静态量化。 网址静态离线量化的目标是求取量化比例因子，主要通过对称量化、非对称量化方式来求，而找最大值或者阈值的方法又有MinMax、KLD、ADMM、EQ等方法&lt;/p>
&lt;h2 id="支撑量化的一些算子和库" class="heading-element">&lt;span>支撑量化的一些算子和库&lt;/span>
 &lt;a href="#%e6%94%af%e6%92%91%e9%87%8f%e5%8c%96%e7%9a%84%e4%b8%80%e4%ba%9b%e7%ae%97%e5%ad%90%e5%92%8c%e5%ba%93" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="marlin" class="heading-element">&lt;span>Marlin&lt;/span>
 &lt;a href="#marlin" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>支持混合精度运算，例如 FP16 * INT4 运算，FP8 * INT4运算&lt;/p>
&lt;p>一种支持 W4A16的 GEMM kernel（一定程度上kernel 实现和量化算法是独立的），因此 marlin kernel 也支持 AWQ 量化模型执行。原始的Marlin Kernel只支持W4A16计算模式，而 QQQ 在 Marlin kernel 的基础上，支持了 W4A8 的计算模式。&lt;/p>
&lt;h2 id="moe量化" class="heading-element">&lt;span>MoE量化&lt;/span>
 &lt;a href="#moe%e9%87%8f%e5%8c%96" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>以实习期间做过的 MoE wna16marlin kernel 为例（sgl PR 7683）&lt;/p>
&lt;ul>
&lt;li>MoE 专用路由: 支持动态专家选择和 token 路由&lt;/li>
&lt;li>多量化格式: 支持 INT4/INT8/FP8 等多种量化类型&lt;/li>
&lt;li>Tensor Core 优化: 使用 CUDA tensor core 指令加速矩阵运算&lt;/li>
&lt;li>内存布局优化: 针对 MoE 访问模式优化的内存布局&lt;/li>
&lt;li>原子操作: 支持原子加法减少全局归约开销&lt;/li>
&lt;/ul></description></item><item><title>推理引擎请求调度优化</title><link>https://yitaonote.com/2025/8cb0fe8/</link><pubDate>Sun, 24 Aug 2025 11:36:09 +0800</pubDate><guid>https://yitaonote.com/2025/8cb0fe8/</guid><category domain="https://yitaonote.com/categories/%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96/">推理优化</category><description>&lt;h2 id="批处理" class="heading-element">&lt;span>批处理&lt;/span>
 &lt;a href="#%e6%89%b9%e5%a4%84%e7%90%86" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/1941421923872514352"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/1941421923872514352&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="静态批处理static-batching" class="heading-element">&lt;span>静态批处理（Static Batching）&lt;/span>
 &lt;a href="#%e9%9d%99%e6%80%81%e6%89%b9%e5%a4%84%e7%90%86static-batching" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>请求被放入批处理队列中，当批处理队列满了之后再运行。&lt;/p>
&lt;p>静态批处理是批处理请求最简单的实现。但它会大幅增加延迟，从而限制其使用场景。&lt;/p>
&lt;p>如果说单独运行每个请求就像每个人开自己的车，那么批处理就像一辆公交车。如果公交车采用静态批处理，司机会等待车内乘客满载，然后开往目的地。这确保了公交车每次行驶时都满载。同样，用于模型推理的静态批处理会等到收到一定数量的请求后，再运行单个批处理来同时处理这些请求。&lt;/p>
&lt;p>当对于某个业务流程来说延迟不是问题时，例如每天处理大量文档，静态批处理是最合适的。静态批处理会增加在系统其他位置协调请求的复杂性。使用静态批处理需要一个管理良好的请求队列来为模型提供数据，并且需要一个以大块形式接收模型输出的方法。&lt;/p>
&lt;h3 id="动态批处理dynamic-batching" class="heading-element">&lt;span>动态批处理（Dynamic Batching）&lt;/span>
 &lt;a href="#%e5%8a%a8%e6%80%81%e6%89%b9%e5%a4%84%e7%90%86dynamic-batching" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>请求在收到时被分批放置到队列中，在队列满了或自第一个请求以来经过足够的时间后进行批处理。&lt;/p>
&lt;p>静态批处理非常适合日常作业或后台处理。但对于延迟敏感（这里主要是和用户的交互相关业务场景）的生产部署（例如：根据用户输入生成图像），静态批处理并不适用。&lt;/p>
&lt;p>回到我们之前关于公交车的比喻，想象一下，在车流量不大的日子里，你是第一个上车的人。如果你必须等到车上坐满才能出发，那你得等很长时间。但如果司机在第一个乘客上车时启动一个计时器，并在车上坐满或计时器用完（以先到者为准）时出发，那会怎么样呢？这样，你最多只需要等几分钟。&lt;/p>
&lt;p>动态批处理的工作方式相同。您可以使用以下命令设置动态批处理：&lt;/p>
&lt;ol>
&lt;li>预设的最大批量大小，您希望在每次进行批处理之前达到该大小。&lt;/li>
&lt;li>在运行部分批处理之前接收第一个请求后等待的窗口。&lt;/li>
&lt;/ol>
&lt;p>假设你设置的模型服务器的批处理大小为 16 个请求，窗口为 100 毫秒。当服务器收到第一个请求时，它将：&lt;/p>
&lt;ol>
&lt;li>在 100 毫秒内接收 15 个以上请求并立即运行完整批次，或者&lt;/li>
&lt;li>接收少于 15 个请求，并在 100 毫秒后运行部分批处理。&lt;/li>
&lt;/ol>
&lt;p>动态批处理非常适合 Stable Diffusion XL 等模型的实时流量，因为每个推理请求所需的时间大致相同。具体部署的正确设置取决于流量模式和延迟要求，但动态批处理可为您提供多种选项的灵活性。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4orz4rccug30g00a04ew.gif' alt="image">&lt;/p>
&lt;h3 id="连续批处理continuous-batching" class="heading-element">&lt;span>连续批处理（Continuous Batching）&lt;/span>
 &lt;a href="#%e8%bf%9e%e7%bb%ad%e6%89%b9%e5%a4%84%e7%90%86continuous-batching" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;blockquote>
&lt;p>来源于 Orca OSDI'22&lt;/p>&lt;/blockquote>
&lt;p>请求按令牌逐个进行处理，当旧请求完成并释放 GPU 上的空间时，新请求就会得到处理。&lt;/p>
&lt;p>虽然动态批处理非常适合图像生成等场景，其中每个输出大约需要相同的时间来创建，但我们可以通过连续批处理为 LLM 做得更好。&lt;/p>
&lt;p>LLM 会创建一系列 token 作为输出。这些输出序列的长度会有所不同——模型可以回答一个简单的问题，也可以通过逐步推理进行详细的分析。如果使用动态批处理方法，则每批请求都需要等待最长的输出完成后才能开始下一批请求。这会导致 GPU 资源闲置。&lt;/p>
&lt;p>连续批处理在令牌级别而非请求级别进行。LLM 推理的瓶颈在于模型权重的加载。因此，对于连续批处理，模型服务器会按顺序加载模型的每一层，并将其应用于每个请求的下一个令牌。在连续批处理中，相同的模型权重可用于生成一个响应的第 5 个令牌和另一个响应的第 85 个令牌。&lt;/p>
&lt;p>在公交车的例子中，连续批处理类似于现实世界中公交线路的运作方式。当司机沿着路线行驶时，乘客的乘坐时间会有所不同。当一位乘客到达目的地时，就会为另一位乘客腾出座位。&lt;/p>
&lt;p>通过消除等待每个批次的最长响应完成的空闲时间，连续批处理比动态批处理提高了 GPU 的利用率。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4orzdt1xlg30hs0b41l3.gif' alt="image">&lt;/p>
&lt;h2 id="pd分离" class="heading-element">&lt;span>PD分离&lt;/span>
 &lt;a href="#pd%e5%88%86%e7%a6%bb" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/19796399275"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/19796399275&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h2 id="sgl和vllm区别" class="heading-element">&lt;span>SGL和vllm区别&lt;/span>
 &lt;a href="#sgl%e5%92%8cvllm%e5%8c%ba%e5%88%ab" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考：&lt;a href="https://www.zhihu.com/question/666943660/answer/1940915117643530378"target="_blank" rel="external nofollow noopener noreferrer">https://www.zhihu.com/question/666943660/answer/1940915117643530378&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h2 id="mnn" class="heading-element">&lt;span>MNN&lt;/span>
 &lt;a href="#mnn" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>MNN 是一个为移动端和嵌入式设备设计的轻量级深度学习推理引擎。它的核心目标是在资源有限的硬件上，如手机、物联网设备和智能家居，高效地运行神经网络模型。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>设计理念： 追求极致的轻量化和高性能。它通过各种优化技术，如量化、模型压缩、算子融合和硬件加速，来减少模型大小、降低内存占用和提升计算速度。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持模型： 主要用于视觉、语音和传统机器学习任务，例如图像分类、目标检测、人脸识别、风格迁移和语音识别。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>优化重点：&lt;/p>
&lt;/li>
&lt;li>
&lt;p>异构计算： 充分利用 CPU、GPU 和 DSP 等不同硬件的特性。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>内存优化： 采用内存池等技术，减少内存分配和碎片化。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>量化技术： 支持 FP32、FP16、Int8 等多种数据类型，以适应不同硬件平台并提升性能。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>LLM 推理引擎 专门为大型语言模型设计，如 Llama、GPT 和 Mistral 等。这些模型通常拥有数百亿甚至上万亿的参数，对计算和内存资源的需求极高。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>设计理念： 旨在解决 LLM 推理中的独特挑战，包括巨大的模型尺寸、高昂的计算量和 KV 缓存（Key-Value Cache）的内存开销。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持模型： 专注于处理基于 Transformer 架构的超大规模语言模型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>优化重点：&lt;/p>
&lt;/li>
&lt;li>
&lt;p>并行计算： 利用多 GPU 和分布式计算来加载和运行庞大的模型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>KV 缓存管理： 优化注意力机制中的 KV 缓存，以减少内存占用并提升推理速度。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>FlashAttention 等高效算法： 采用专门为 Transformer 设计的优化算法，以减少显存访问并提升计算效率。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>量化和剪枝： 虽然 MNN 也使用这些技术，但 LLM 推理引擎中的量化通常更复杂，需要确保量化后模型的性能损失最小。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>动态批处理： 动态地调整批处理大小，以最大化 GPU 利用率。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="计算图" class="heading-element">&lt;span>计算图&lt;/span>
 &lt;a href="#%e8%ae%a1%e7%ae%97%e5%9b%be" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="什么是计算图" class="heading-element">&lt;span>什么是计算图？&lt;/span>
 &lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e8%ae%a1%e7%ae%97%e5%9b%be" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>首先，计算图是一种有向无环图（DAG, Directed Acyclic Graph），它用来描述运算操作之间的关系。在图中：&lt;/p>
&lt;p>节点（Nodes） 代表数据（张量 Tensor）或者操作（Operations）。&lt;/p>
&lt;p>边（Edges） 代表数据流向，表示数据从一个操作传递到另一个操作。&lt;/p>
&lt;p>举一个最简单的例子，假设我们有这样一个数学表达式： y = (a + b) * c&lt;/p>
&lt;p>这个表达式可以用如下的计算图来表示：&lt;/p>
&lt;h3 id="pytorch-计算图的核心特点动态性" class="heading-element">&lt;span>PyTorch 计算图的核心特点：动态性&lt;/span>
 &lt;a href="#pytorch-%e8%ae%a1%e7%ae%97%e5%9b%be%e7%9a%84%e6%a0%b8%e5%bf%83%e7%89%b9%e7%82%b9%e5%8a%a8%e6%80%81%e6%80%a7" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>这是 PyTorch 与 TensorFlow 1.x 等早期框架最显著的区别。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>静态图 (Static Graph - 如 TensorFlow 1.x):&lt;/p>
&lt;ul>
&lt;li>
&lt;p>定义与运行分离 (Define-and-Run)。 你需要先完整地定义好整个计算图的结构，然后才能向这个固定的图中输入数据并执行它。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>优点： 可以在运行前对整个图进行优化，例如分配显存、融合算子，因此潜在的性能可能更高。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>缺点： 不够灵活。对于需要根据输入数据动态改变计算流程的模型（如循环神经网络 RNN 中处理不同长度的序列），定义起来非常麻烦和不直观。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>动态图 (Dynamic Graph - PyTorch):&lt;/p>
&lt;ul>
&lt;li>
&lt;p>定义与运行合一 (Define-by-Run)。 计算图是在代码运行时动态生成的。每当你执行一个操作，一个新的节点和相应的边就被添加到图中。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>优点：&lt;/p>
&lt;/li>
&lt;li>
&lt;p>直观灵活： 代码所见即所得，你可以像写普通 Python 程序一样使用 if/else、for 循环等控制流语句，计算图会根据你的执行路径自然地构建出来。这使得调试非常方便。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>易于处理动态输入： 对于 RNN 等模型极为友好。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>缺点： 理论上，由于图是动态生成的，难以进行全局优化，可能会有一些性能开销。但随着 PyTorch 的发展（如 torch.jit.script 和 TorchDynamo），这个差距正在被不断缩小。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="pytorch-计算图是如何构建的" class="heading-element">&lt;span>PyTorch 计算图是如何构建的？&lt;/span>
 &lt;a href="#pytorch-%e8%ae%a1%e7%ae%97%e5%9b%be%e6%98%af%e5%a6%82%e4%bd%95%e6%9e%84%e5%bb%ba%e7%9a%84" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>PyTorch 的计算图是在前向传播（Forward Pass） 过程中，由 autograd 系统自动构建的。这个图记录了所有张量以及对它们进行的操作历史。&lt;/p>
&lt;p>我们来看关键的几个组件：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>torch.Tensor: 这是图中的核心数据结构。一个张量有几个重要属性：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>data: 存储张量的实际数据。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>requires_grad: 一个布尔值。如果为 True，表示该张量需要计算梯度。autograd 系统会从这个张量开始追踪所有操作。通常，模型的权重参数该值为 True，而输入数据默认为 False。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>grad: 存储计算出的梯度值。初始时为 None。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>grad_fn: 这是构建计算图的关键！ 它引用了一个 Function 对象，该对象记录了创建这个张量的操作。如果一个张量是用户直接创建的（叶子节点），那么它的 grad_fn 为 None。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="计算图如何用于反向传播" class="heading-element">&lt;span>计算图如何用于反向传播？&lt;/span>
 &lt;a href="#%e8%ae%a1%e7%ae%97%e5%9b%be%e5%a6%82%e4%bd%95%e7%94%a8%e4%ba%8e%e5%8f%8d%e5%90%91%e4%bc%a0%e6%92%ad" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>计算图最重要的作用就是为了计算梯度。当你对某个张量（通常是最终的损失 &lt;code>loss&lt;/code>）调用 &lt;code>.backward()&lt;/code> 方法时，&lt;code>autograd&lt;/code> 引擎就会启动。&lt;/p>
&lt;p>过程如下：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>启动： 从调用 .backward() 的张量（例如 y）开始，传入一个初始梯度（对于标量，默认为 1.0）。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>反向遍历： autograd 引擎沿着计算图，从根节点 y 向叶子节点 a, b, c 反向传播。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>链式法则： 在每个节点处，autograd 会使用该节点的 grad_fn 来计算输入张量相对于当前节点的梯度。这本质上就是应用微积分中的链式法则。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>例如，在 y 节点，autograd 计算 y 对 d 和 c 的偏导数。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>然后传播到 d 节点，autograd 计算 d 对 a 和 b 的偏导数。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>梯度累积： 计算出的梯度会被**累积（accumulate）**到各个叶子节点的 .grad 属性中。这就是为什么在每个训练迭代开始时，我们通常需要调用 optimizer.zero_grad()，否则梯度会一直叠加。&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>GPU及CUDA基本概念</title><link>https://yitaonote.com/2025/34980c2/</link><pubDate>Fri, 22 Aug 2025 01:02:38 +0800</pubDate><guid>https://yitaonote.com/2025/34980c2/</guid><category domain="https://yitaonote.com/categories/%E6%89%BE%E5%B7%A5%E4%BD%9C/">找工作</category><description>&lt;h2 id="gpu-架构" class="heading-element">&lt;span>GPU 架构&lt;/span>
 &lt;a href="#gpu-%e6%9e%b6%e6%9e%84" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/12083951223"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/12083951223&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="smstreaming-multiprocessor结构" class="heading-element">&lt;span>SM（Streaming Multiprocessor）结构&lt;/span>
 &lt;a href="#smstreaming-multiprocessor%e7%bb%93%e6%9e%84" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mhy3ink1j31401t6tff.jpg' alt="image">&lt;/p>
&lt;p>其中包含一些核心组件：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>CUDA core&lt;/strong>（即&lt;strong>Streaming Processor，SP&lt;/strong>）：其中包含整数处理单元和单精度浮点数处理单元，用于执行基本的数值运算。不同架构中CUDA core数量不同，这个数量在一定程度上体现了GPU的计算能力（并不是完全决定，还有如时钟频率，内存带宽，指令集等其他影响因素）&lt;/li>
&lt;/ul>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mi3cr005j30ki0d7q3n.jpg' alt="image">&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>Register File&lt;/strong>：寄存器文件。存放指令操作数；也有一些特殊寄存器用于存放系统变量，例如grid维度，thread blcok维度，线程id等等。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Loca/Store Units（LD/ST）&lt;/strong>：执行内存（显存、shared memory）读写数据命令。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Special Function Units（SFU）&lt;/strong>：执行一些特殊函数，如sqrt，sin，cos等。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Warp Scheduler&lt;/strong>：GPU线程调度执行时是以一组线程（warp）为单位的，Warp Scheduler从驻留在SM上的warp中选
择合适的warp用于执行。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Dispatch Unit&lt;/strong>：负责从Warp Scheduler选中的线程中取出要执行的指令发射到CUDA core去执行。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Shared Memory/L1 Cache&lt;/strong>：shared memory可以用于同一个thread block中的线程间互相通信，是可以通过编程读写的。L1 Cache则不能被编程，由GPU决定存放和淘汰内容。这里把Shared Memory和L1 Cache放在一起是因为它们在物理存储上是共用的，&lt;strong>可以通过API控制两者的配比。&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>L2 Cache&lt;/strong>：SM 到全局内存中间的缓存，latency 会比访问全局内存小，缓存全局内存以及local memory所有SM共享一个L2 Cache&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>常量、纹理缓存与全局内存缓存&lt;/strong>：有的gpu架构这三类内存结构也同样存在于一个SM中：&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>以上以 Fermi 架构举例，后续的新架构都是在其基础上的进一步发展，例如更多的CUDA core数量，更大的内存容量，更高的IO带宽，以及增加一些新的组件如Tensor Core，RT Core等&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h2 id="hopper架构" class="heading-element">&lt;span>Hopper架构&lt;/span>
 &lt;a href="#hopper%e6%9e%b6%e6%9e%84" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/1900569635532800833"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/1900569635532800833&lt;/a>&lt;/p>&lt;/blockquote>
&lt;p>由于实习期间使用的都是 H20 集群，因此学习一下 Hopper 架构的特点&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mng9yb41j31400lvn2c.jpg' alt="image">&lt;/p>
&lt;ol>
&lt;li>新增了 Block Cluster 这一线程层次，提供跨 Block 的 shared memory 的访问。Hopper 架构在 Cluster 内部，位于 L1 和 L2 Cache 之间新增了一层SM-to-SM Network。Thread Block Cluster 内部的 SM 可以通过该层网络访问其他 SM 的 Shared Memory&lt;/li>
&lt;li>访存计算异步执行：Hopper 在硬件层提供了 TMA 单元，在软件层可以通过 cuda::memcpy_async 使用 TMA 单元实现异步的 Global Memory 和 Shared Memory 之间的拷贝。&lt;/li>
&lt;/ol>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4z973y3bdj30vy0oxqbf.jpg' alt="image">&lt;/p>
&lt;h2 id="gpu线程调度" class="heading-element">&lt;span>GPU线程调度&lt;/span>
 &lt;a href="#gpu%e7%ba%bf%e7%a8%8b%e8%b0%83%e5%ba%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>一个CUDA kernel对应一个grid，一个grid分成若干个thread block，每个thread block中包含若干线程。那么从硬件角度看，GPU在调度Kernel时，粗粒度看分成两个阶段，即&lt;strong>SM分配&lt;/strong>和&lt;strong>线程调度执行&lt;/strong>。&lt;/p>
&lt;h3 id="sm分配" class="heading-element">&lt;span>SM分配&lt;/span>
 &lt;a href="#sm%e5%88%86%e9%85%8d" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>GPU会根据SM资源情况来进行block的分配，只有当SM上剩余资源足够满足一个block的总资源需求时，SM才有可能被分配给当前block。这里的资源主要指&lt;mark class="mark-default">共享内存&lt;/mark>和&lt;mark class="mark-default">寄存器&lt;/mark>，即SM上的Shared Memory及寄存器是不能超额分配的；但CUDA core，SFU等指令执行单元则是可以超额分配的。&lt;/p>
&lt;p>例如Fermi架构中每个SM上CUDA core只有32个，但是一个SM最大可以支持1536个线程，因为虽然不能同时执行这1536个线程的指令，但是可以对CUDA core进行时分复用，执行不同线程时进行上下文切换即可，GPU上的线程上下文切换效率远高于CPU上的线程切换，因为线程的寄存器已经事先分配好，切换时并不需要像CPU上那样把寄存器状态在主机内存上进行换入换出操作。&lt;/p>
&lt;p>当然一个SM上最多能够支持多少个线程还是有硬性限制的，具体限制数值取决于具体架构。一个thread block分配到一个SM上之后就会驻留下来直到执行完毕，同一个grid中的thread block可能分配到不同的SM上，反过来同一个SM也可能被分配给来自不同grid的多个thread block；一个thread blcok分配到一个SM上之后，则称为Active thread block，一个SM被分配thread block之后则称为Active SM。&lt;/p>
&lt;h3 id="线程调度执行" class="heading-element">&lt;span>线程调度执行&lt;/span>
 &lt;a href="#%e7%ba%bf%e7%a8%8b%e8%b0%83%e5%ba%a6%e6%89%a7%e8%a1%8c" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>在调度执行时，GPU并不会针对单个线程去调度，而是把一个thread block进一步划分为若干个Warp（线程束）为单位进行调度，一个warp包含32个线程（目前所有架构上都是32），每个Warp Sheduler每个时钟周期（cycle）选择一个Warp将其指令通过Dispatch Unit发射到执行单元。也就是说一个thread block中的线程只是在逻辑上是同步执行，但是在硬件层面则不一定。可以从资源分配和调度的角度对此与主机上的调度做一个类比，thread block类似于主机上的进程，是资源分配单位，warp类似于主机上的线程，是调度执行单位。&lt;/p>
&lt;p>Warp有如下几种状态：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Active&lt;/strong>：一个Acitve的thread block中的Warp都是Active的，也即已经分配到SM的Warp都是Active Warp。&lt;/li>
&lt;li>&lt;strong>Stalled&lt;/strong>：当前Cycle暂时不能执行下一条指令的Warp，有多种情况可能导致Warp处于Stalled状态，常见的有：
&lt;ul>
&lt;li>thread block内的线程同步，部分先执行的Warp必须等待同一个block内的后执行的Warp到达同一个同步点。&lt;/li>
&lt;li>线程需要的数据还没有传输完成，需要等待数据。&lt;/li>
&lt;li>下一条指令依赖于上一条指令的输出，但是上一条指令还没有执行完成。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Eligible&lt;/strong>：当前Cycle已经准备好可以执行下一条指令的Warp。Warp Scheduler在选择Warp时只会从Eligible Warp中进行选取。&lt;/li>
&lt;li>&lt;strong>Selected&lt;/strong>：被Warp Scheduler选中准备执行其中线程下一条指令的Warp。&lt;/li>
&lt;/ul>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mmyhwmk8j30bp0klgm9.jpg' alt="image">&lt;/p>
&lt;p>注意一个Warp中线程的同步性仅仅体现在调度上，即一个Warp中的所有线程是同时被Scheduler选中的，但是在指令发射和执行上则不一定了，至少可以从几个方面去看：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Instruction replay：同一条指令发射多次称为instruction replay。CUDA指令可以分为不同类型，不同类型指令使用不同类型的执行单元执行，例如memory的读写指令是通过LD/ST units执行，特殊函数如sin，cos是通过SFU执行，即并非所有指令都是通过CUDA core执行。而对于某种类型执行单元来说，一个SM上可能都没有32个，例如Fermi中一个SM上只有4个SFU，那么一个Warp执行cos指令时，就需要发射和执行8次。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Warp divergence：当一个Warp中的线程因为分支条件(如if else)不同而走到不同的路径时会造成warp divergence，此时不同分支的线程会形成不同分组，这些分组串行在不同的cycle执行&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h3 id="延迟隐藏" class="heading-element">&lt;span>延迟隐藏&lt;/span>
 &lt;a href="#%e5%bb%b6%e8%bf%9f%e9%9a%90%e8%97%8f" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>延迟隐藏就是指通过并行执行，充分利用硬件执行单元，使得不同操作能够在时间上重叠，最大化某一种或几种操作的吞吐量（延迟隐藏的主要优化目标是吞吐量，而不是单个操作的时长）。比如这里的&lt;strong>当Warp等待内存时，SM立即切换其他就绪Warp&lt;/strong>。&lt;/p>
&lt;p>了解详情可以进一步学习：&lt;strong>利特尔法则（little&amp;rsquo;s law）&lt;/strong>&lt;/p>
&lt;div class="details admonition note open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-solid fa-pencil-alt" aria-hidden="true">&lt;/i>以上三个步骤：&lt;strong>SM资源分配、线程调度执行、延迟隐藏&lt;/strong> 构成了 block 调度执行的完整流程。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h3 id="warp的划分" class="heading-element">&lt;span>warp的划分&lt;/span>
 &lt;a href="#warp%e7%9a%84%e5%88%92%e5%88%86" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>CUDA线程与数组在内存中的排布类似，也分为逻辑视图和硬件视图，在逻辑视图上，一个thread block可能是1到3维的，但是在硬件视图上，这些线程上仍然是1维排布的，即按row-major方式将连续的32个线程组成一个Warp，用公式表达就是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// thread_id是按row major方式将线程在block中的3维坐标转为1维线性坐标，
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 维度从内到外为x, y, z（下面这个转换方式对于1,2,3维thread block都是通用的）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">thread_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">z&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// warp_id相同的线程就属于同一个Warp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// warpSize也是一个内置变量，可以在kernel函数中直接引用，表示warp大小，目前来说是32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">warp_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">thread_id&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="n">warpSize&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;div class="alert alert-tip">&lt;p class="alert-title">&lt;svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">&lt;path d="M8 1.5c-2.363.0-4 1.69-4 3.75.0.984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75.0 01-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456.0 00-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863.0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751.0 01-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304.0-2.06-1.637-3.75-4-3.75zM5.75 12h4.5a.75.75.0 010 1.5h-4.5a.75.75.0 010-1.5zM6 15.25a.75.75.0 01.75-.75h2.5a.75.75.0 010 1.5h-2.5A.75.75.0 016 15.25z"/>&lt;/svg>提示&lt;/p>&lt;p>当一个thread block中的线程总数不是32的整数倍时，从编程视角看，最后一个Warp是不满32个线程的，但是在GPU硬件上仍然会占用32个线程的资源，只是其中部分线程会被标识为不活跃线程，这种情况下就会造成一些资源的浪费，所以在实际使用时，要尽量保证thread block中的线程数为32的整数倍。&lt;/p>&lt;/div>&lt;h3 id="warp-divergence" class="heading-element">&lt;span>warp divergence&lt;/span>
 &lt;a href="#warp-divergence" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Warp Divergence是指同一个Warp中的不同线程由于分支条件（例如if else）不同而进入不同的代码分支的情况。这个在CPU上的多线程不是太大问题，但是在CUDA中则会导致性能下降，因为在发生Warp Divergence时，Warp中走不同分支的线程会串行化执行，以一个2路分支为例，如下伪代码：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">condition&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="p">...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>假设Warp内前一半线程condition 为true ，后一半线程condition为false，那么执行时两部分线程就分开串行执行了，每一部分线程执行时，另一部分线程标识为不活跃线程，但是任然占用执行单元。这种情况下一方面因为串行化增加了整体执行时间，另一方面不活跃线程降低了资源利用率。如下图所示（coherent部分表示没有分支的代码）：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mle9i5pqj30ru0flgmu.jpg' alt="image">&lt;/p>
&lt;p>一个Warp一次执行一条公共指令。如果Warp中的线程由于数据依赖而发生条件分支发散，则warp会执行每个需要的分支路径，同时禁用不在该路径执行的线程。&lt;strong>因此当一个 Warp中的32个线程都执行相同的执行路径时，Warp的效率是最高的。&lt;/strong>&lt;/p>
&lt;p>编译器可能会进行循环展开，或者会通过&lt;strong>分支预测&lt;/strong>来优化短的if或switch块。在这些情况下，所有Warp都不会发散。程序员还可以使用#pragma unroll指令控制循环展开。如下图所示，每个线程执行不同数量的循环迭代，循环迭代的数量在四和八之间变化。在前四次迭代中，所有线程都是活动的并执行A。在剩余的迭代中，一些线程执行A，而其他线程因为已经完成它们的迭代而不活动。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mlbow3lsj30v80gwjs3.jpg' alt="image">&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4mlbvgqohj30vr0fkt9i.jpg' alt="image">&lt;/p>
&lt;div class="details admonition warning open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-solid fa-exclamation-triangle" aria-hidden="true">&lt;/i>Warp Divergence对性能有损害，所以在可能的前提下，应该尽量通过重新设计程序逻辑，算法等方式避免或减少这种情况的发生。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h3 id="sm占用率" class="heading-element">&lt;span>SM占用率&lt;/span>
 &lt;a href="#sm%e5%8d%a0%e7%94%a8%e7%8e%87" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;h2 id="roofline-model" class="heading-element">&lt;span>Roofline Model&lt;/span>
 &lt;a href="#roofline-model" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/24066764550"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/24066764550&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="概念" class="heading-element">&lt;span>概念&lt;/span>
 &lt;a href="#%e6%a6%82%e5%bf%b5" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Roofline Model能帮助我们判断cuda 程序是&lt;strong>显存瓶颈(memory bound)&lt;strong>还是&lt;/strong>计算瓶颈(compute bound)&lt;/strong>，以及判断当前资源利用率的情况。&lt;/p>
&lt;p>Roofline Model的横坐标是计算强度(computational intensity)，单位是FLOP/B，即每读一个Byte所能产生的FLOP数。例如以下base matrix multiply代码:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">__global__&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">cudaMatrixMultiplyBase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">A&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">C&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">M&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">K&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">blockIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">blockIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">M&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">sum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">K&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sum&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">K&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">k&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sum&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>它的&lt;strong>computational intensity&lt;/strong>是0.25，因为每次2次计算（乘加）需要读8个Byte&lt;/p>
&lt;p>Roofline Model是描述一个进程的性能与它所在的硬件的关系，横坐标是计算强度，纵坐标是FLOP/s，如下图所示：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4monuvqgvj31400r475a.jpg' alt="image">&lt;/p>
&lt;p>其中 &lt;strong>红线（屋檐）和绿线（屋顶）&lt;/strong> 与具体硬件有关，红线的斜率是内存带宽，不同的硬件斜率不同（带宽越大，斜率越高），越靠近红线说明对带宽资源利用率越高；&lt;strong>任何一个点到原点的斜率不可能超过硬件最大带宽&lt;/strong> $\beta$&lt;/p>
&lt;p>绿线是硬件的计算峰值，高度越靠近绿线，说明对计算资源利用率越高；&lt;strong>任何一个点的P值(纵坐标)不可能超过&lt;/strong> $\pi$&lt;/p>
&lt;p>&lt;strong>计算强度D是有可能超过 $I_{max}$ 的&lt;/strong>，假设某台机器A计算峰值10GFLOPS，内存带宽是10GB/s，那么 $I_{max}=1$ 。假设某个模型疯狂重复计算，对1GB的数据计算5次乘加，计算量 C=10GFLOPs，那么计算强度 $I=10$&lt;/p>
&lt;hr>
&lt;p>为了加深理解，分别举几个示例说明图中的7个点是如何产生的：&lt;/p>
&lt;p>假设：机器A计算峰值10GFLOPS，内存带宽是10GB/s，那么 $I_{max}=1$，下面的示例都发生在机器A上：&lt;/p>
&lt;p>有一个&lt;strong>计算强度高的模型&lt;/strong> $D_h$，读取10GB的数据产生100GFLOPs的计算， $I=10$；&lt;/p>
&lt;p>另外有一个&lt;strong>计算强度低的模型&lt;/strong> $D_l$，读取10GB的数据只产生5GFLOPs的计算， $I=0.5$；&lt;/p>
&lt;ul>
&lt;li>
&lt;p>$x_2, x_3$ 不可能在现实中出现，正如上面所说，$x_2$ 超过了带宽限制，$x_3$ 超过了硬件计算限制，&lt;/p>
&lt;/li>
&lt;li>
&lt;p>举个例：还是对于机器A，计算峰值10GFLOPS，内存带宽是10GB/s，假设有一个计算强度很高的模型，读取1GB数据计算100GFLOPs，计算强度为100，但实际上由于受到计算峰值的限制，P=10GFLOPS，此时计算的斜率为 $\dfrac{10}{100}=0.1&lt;1=\beta$&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$x_1, x_4$ 是在计算强度很低的模型中可能出现，以模型 $D_l$ 为例，$x_1$ 出现的原因是由于程序实现问题导致计算性能都很低：例如计算5GFLOPS用了2s（理想情况是0.5s）导致纵坐标P比较低：($I=0.5, P=2.5$)，&lt;strong>属于工程提升空间很大，不需要增加硬件性能就可大幅度提升性能。&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$x_1$ 优化后变为 $x_4$，计算5GFLOPS只用了1s（理想情况是0.5s，但是受限于带宽，1s只能传输10GB数据），已经十分接近屋檐这条线了，但计算强度太低了，优化方向是提升计算强度（只能改变模型，属于模型的计算强度透支）来增加性能，如果算法没办法改进的情况下，那提升性能的方式只能堆更高的带宽了。 $x_4$ 属于&lt;strong>内存带宽受限型（模型不动的情况下，无脑增加带宽）&lt;/strong>。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$x_5$ 属于把硬件资源都用满了（计算和带宽全都吃满），且模型的计算强度也被挖掘得刚刚好。其性能即受限于硬件带宽又受限于计算资源，增加任何一项硬件资源（带宽和计算）都没法提升性能。“刚刚好”的意思是计算强度和硬件刚好匹配，其即使增加了硬件计算和带宽，不改变模型的情况下，也没办法提升性能。它即受限于硬件，又受限于模型。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$x_6$ 也属于把硬件资源都用满了，但模型本身的计算强度还有很大潜力，属于硬件压制了模型。&lt;strong>只需要堆计算资源性能就可以提升，模型和带宽都不用动，属于计算受限型&lt;/strong>。典型示例就是计算强度高的模型 $D_h$ ，计算强度为10，某个实现使得带宽和计算都已经跑满了（每秒读取10GB，计算10GFLOPs，受限于硬件算力和带宽）&lt;/p>
&lt;/li>
&lt;li>
&lt;p>$x_7$ 和 $x_1$一样，硬件利用率太低了，需要改进实现，则能成长为 $x_6$ 一样的潜力型选手。例如计算强度高的模型 $D_h$，计算强度为10，但某个实现使得计算没有打满（例如带宽没有打满，10GB数据花了2s，即便计算打满也导致GFLOPS为5GFLOPS，或者计算资源没有吃满，例如10GB数据用了1s传输，2s计算&amp;mdash;重复计算、bankconflict之类的导致）&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>总结：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Roofline model中，&lt;strong>越靠近屋檐（红色）或者屋顶（蓝色）的线，说明已经将硬件资源吃得很满&lt;/strong>，除非修改模型改变计算强度，或者修改硬件，否则很难提升。远离这些线的点，在实现是有提升空间的，离得远，说明实现对硬件的利用率越差。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Roofline model中，在 $I_{max}$ 左边的点，都是&lt;strong>内存带宽限制性&lt;/strong>；在 $I_{max}$ 右边的点，属于&lt;strong>计算限制型&lt;/strong>。如果模型在 $I_{max}$ 的左边且靠近屋檐（红色）线，那么不改模型算法的情况下提升带宽资源能增加性能，如果在 $I_{max}$右边且靠近屋顶（蓝色）的线，那么不改模型算法的情况下提升计算资源能增加性能&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>不可能有点会处于超过屋檐（红色）或者屋顶（蓝色）的区域&lt;/strong>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="bank-conflict-与-避免方法" class="heading-element">&lt;span>Bank Conflict 与 避免方法&lt;/span>
 &lt;a href="#bank-conflict-%e4%b8%8e-%e9%81%bf%e5%85%8d%e6%96%b9%e6%b3%95" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>&lt;a href="https://www.bilibili.com/video/BV1Xz4y157p7/?vd_source=d436f8a78656c9132eae4a84f939d219"target="_blank" rel="external nofollow noopener noreferrer">参考视频&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="bank概念" class="heading-element">&lt;span>Bank概念&lt;/span>
 &lt;a href="#bank%e6%a6%82%e5%bf%b5" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>cuda内核在执行的时候往往是以warp为单位去调度和执行，一个warp 32 个线程。所以，为了能够高效访存，shared memory中也对应了分成了32个存储体，这个存储体称之为 &lt;em>bank&lt;/em> 。&lt;/p>
&lt;p>bank 是CUDA中一个重要概念，是内存的访问时一种划分方式，&lt;mark class="mark-default">在GPU中，访问某个地址的内存时，为了减少读写次数，访问地址并不是随机的，而是一次性访问bank内的内存地址，类似于内存对齐一样&lt;/mark>。一般GPU认为如果一个程序要访问某个内存地址时，其附近的数据也有很大概率会在接下来会被访问到。&lt;/p>
&lt;p>&lt;strong>（目前N卡都是32个bank）&lt;/strong>，分别对应 warp 中32个线程。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4mzx4rjhtj31ai0hogon.jpg' alt="image">&lt;/p>
&lt;p>bank的宽度，代表的是一个bank所存储的数据的大小宽度。可以是：&lt;/p>
&lt;ul>
&lt;li>4 字节（32bit，单精度浮点数 float32）&lt;/li>
&lt;li>8 字节（64bit，双精度浮点数 float64）&lt;/li>
&lt;/ul>
&lt;p>每 31 个bank，就会进行一次 stride。&lt;/p>
&lt;p>比如说 bank 的宽度是4字节，我们在 share mem 中申请了 &lt;code>float A[256]&lt;/code> 大小的空间，那么：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">A[0], A[1], ..., A[31] 分别在 bank0, bank1, ..., bank31 中
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A[32], A[33], ..., A[63] 也分别在 bank0, bank1, ..., bank31 中
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">所以 A[0] 和 A[32] 是共享一个bank的&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="bank-conflict" class="heading-element">&lt;span>Bank Conflict&lt;/span>
 &lt;a href="#bank-conflict" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>一个很理想的情况是，32个thread，分别访问share mem中32个不同的bank，没有 &lt;strong>bank conflict&lt;/strong>，一个 memory 周期完成所有的 memory read/write（行优先访问）&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n0km2yugj31bs0lmtc4.jpg' alt="image">&lt;/p>
&lt;p>那最不理想的情况就是，32个thread，访问 share mem 中的同一个bank，导致最严重的 bank conflict，需要32个memory 周期才能完成所有的 memory read/write（列优先访问）。如果在block内多个线程访问的地址落入到同一个bank内，那么就会访问同一个bank就会产生bank conflict，这些访问将是变成串行。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n0kps5bsj31bu0hc41t.jpg' alt="image">&lt;/p>
&lt;h3 id="使用-padding-缓解-bank-conflict" class="heading-element">&lt;span>使用 Padding 缓解 Bank Conflict&lt;/span>
 &lt;a href="#%e4%bd%bf%e7%94%a8-padding-%e7%bc%93%e8%a7%a3-bank-conflict" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>为了方便解释，这里使用了8个bank一次stride进行举例。（实际CUDA设计中依然是32个bank一次stride）&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n0kufl2zj31z00tuq89.jpg' alt="image">&lt;/p>
&lt;p>可以在申请share mem时多申请一列（padding），这样就会更改share mem的布局，使得32个线程访问变成串行。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n0kz7u9jj31ok0u0jxc.jpg' alt="image">&lt;/p>
&lt;div class="details admonition note open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-solid fa-pencil-alt" aria-hidden="true">&lt;/i>但会产生未对齐的地址和引入无效数据，导致内存占用增加和干扰需要对齐地址的快速指令。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h3 id="使用-swizzle-缓解-bank-conflict" class="heading-element">&lt;span>使用 Swizzle 缓解 Bank Conflict&lt;/span>
 &lt;a href="#%e4%bd%bf%e7%94%a8-swizzle-%e7%bc%93%e8%a7%a3-bank-conflict" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://www.zhihu.com/question/667972067"target="_blank" rel="external nofollow noopener noreferrer">https://www.zhihu.com/question/667972067&lt;/a>&lt;/p>&lt;/blockquote>
&lt;p>Swizzle通过重新映射内存地址，可以将原本访问相同Bank的线程分散到不同的Bank。Swizzle的核心思想是通过&lt;strong>物理地址映射&lt;/strong>避免Bank Conflict，同时保持逻辑地址不变。在CUTLASS中，Swizzle的实现通过一系列变换对内存布局进行重排。通过行列坐标的异或操作，Swizzle确保每个线程访问不同的Bank，从而实现了Bank Conflict Free的内存访问。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">naive_layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">r&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">columns&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">padding_layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">r&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">columns&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 一个可能的实现 Swizzle 的方式：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">row_swizzled_layout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">half&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">address&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">uint64_t&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">r&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">columns&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">];&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">half&lt;/span> &lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">address&lt;/span> &lt;span class="o">^&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>可以用padding和swizzle的示意图来解释下这种物理地址映射的概念：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n1ktpuitj316o0li1ky.jpg' alt="image">
⬆️bank conflict free
&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n1grv22wj316o0li1ky.jpg' alt="image">
⬆️bank conflict solved by padding
&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n1guc2g0j316o0lg1ky.jpg' alt="image">
⬆️bank conflict solved by swizzle&lt;/p>
&lt;div class="details admonition note open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-solid fa-pencil-alt" aria-hidden="true">&lt;/i>如果warp中的线程经过最多两次冲突就能得到所要的数据则成为2-way bank conflict，如果同一个warp中的所有线程访问一个bank中的32个不同的地址，则需要分32此，则称为32-way bank conflict。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>bank conflict只发生在同一个warp的线程之间&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>参考文献：&lt;/p>&lt;/blockquote>
&lt;ol>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/4746910252"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/4746910252&lt;/a>&lt;/li>
&lt;/ol>
&lt;h2 id="cuda-stream--cuda-graph" class="heading-element">&lt;span>CUDA Stream &amp;amp; CUDA Graph&lt;/span>
 &lt;a href="#cuda-stream--cuda-graph" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/699754357"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/699754357&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="cuda-stream" class="heading-element">&lt;span>CUDA Stream&lt;/span>
 &lt;a href="#cuda-stream" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>cuda编程里最重要的特性就是异步：CPU提交任务到GPU中异步执行。为了控制异步之间的并发顺序，cuda引入了stream和event的概念。本文尝试分析理解stream和event的含义，充分理解并使用stream、event，是正确、高效利用GPU的必要条件。&lt;/p>
&lt;p>&lt;strong>只有一个CPU thread的情况&lt;/strong>&lt;/p>
&lt;p>当不考虑GPU的时候，CPU线程就在不断地执行指令。从时间维度上看，就是这样的一条线：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n263lmc5j30u603e74r.jpg' alt="image">&lt;/p>
&lt;p>&lt;strong>一个CPU thread与一个GPU执行单元&lt;/strong>&lt;/p>
&lt;p>GPU相当于CPU的附属硬件，当我们增加了一个GPU执行单元的时候，由CPU下发任务给GPU，于是从时间维度上看，就出现了两条线：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n267qgnaj30u6090wge.jpg' alt="image">&lt;/p>
&lt;p>这里在GPU和CPU之间还增加了一个GPU队列。当我们在CPU上调用launch kernel的时候，本质上就是把这个kernel放入到GPU的执行队列里。然后，驱动负责维护这个队列，每当执行引擎（硬件资源）空闲的时候，就执行队列里的kernel。&lt;/p>
&lt;p>上图描述了这些关键时间点：&lt;/p>
&lt;ul>
&lt;li>CPU launch kernel 1，kernel 1入队，此时GPU空闲，于是kernel 1马上开始执行&lt;/li>
&lt;li>CPU调用host function，与GPU无关&lt;/li>
&lt;li>CPU launch kernel 2，kernel 2入队，此时GPU还在执行kernel 1，于是kernel 2继续待在队列里&lt;/li>
&lt;li>GPU执行完kernel 1后，驱动程序发现队列里还有kernel 2，于是开始执行kernel 2（这一步不需要CPU参与）&lt;/li>
&lt;/ul>
&lt;p>早期的GPU硬件上只有一个execution engine，因此，不论是哪个进程、哪个线程发起的kernel launch，都在同一个队列里排队。随着GPU的发展，GPU上面开始出现了多个execution engine。&lt;/p>
&lt;p>&lt;strong>一个CPU thread与2个GPU执行单元&lt;/strong>&lt;/p>
&lt;p>当我们有两个或多个GPU执行单元的时候，我们就可以让GPU kernel之间也并行起来：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n28c1ynrj30uo0fs0v2.jpg' alt="image">&lt;/p>
&lt;p>图中蓝色阴影区域就是两个GPU kernel并发执行的时间段。&lt;/p>
&lt;p>这里要注意，kernel 1和kernel 2能不能并发执行，需要由用户来决定，硬件不能擅作主张，否则万一kernel 2要读取kernel 1算出来的数据，那并发执行的结果就是错的。&lt;/p>
&lt;p>为了给用户提供这种控制权，于是我们就有了stream的概念。一个stream就对应于一个执行队列（加一个执行单元），用户可以自行决定是否把两个kernel分开放在两个队列里。&lt;/p>
&lt;blockquote>
&lt;p>stream是cuda为上层应用提供的抽象，应用可以创建任意多个stream，下发任意多个kernel。但如果正在执行的kernel数目超过了硬件的execution engine的数量，那么即使当前stream里没有kernel正在执行，下发的kernel也必须在队列里继续等待，直到有硬件资源可以执行。（注意：此时kernel 执行与CPU下发kernel之间依然是异步的）&lt;/p>&lt;/blockquote>
&lt;p>由此，我们可以总结得到：一个GPU kernel可以执行的必要条件是它在stream的队列首位且存在执行kernel的空闲硬件资源。&lt;/p>
&lt;p>创建stream的时候，我们也可以用 &lt;code>cudaStreamCreateWithPriority&lt;/code> 为它指定优先级。当多个GPU kernel都可以执行的时候，cuda driver负责调度，优先执行high priority的stream里的kernel。&lt;/p>
&lt;h4 id="stream之间的操作cuda-event" class="heading-element">&lt;span>stream之间的操作：cuda event&lt;/span>
 &lt;a href="#stream%e4%b9%8b%e9%97%b4%e7%9a%84%e6%93%8d%e4%bd%9ccuda-event" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>我们不仅可以从CPU thread操作stream，也可以在一个stream上面操作另一个stream。当然，stream只是一层抽象，我们依然要借用CPU thread的辅助，来指导“一个stream上面操作另一个stream”。具体的操作也很简单，就是一个stream上面的kernel想等待另一个stream上面的kernel执行结束之后再执行。&lt;/p>
&lt;p>为了实现这个目标，我们需要记录另一个stream上面的队列状态，这就是cuda event。让我们来看一个具体的例子：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n2a78z2hj31400ehtcv.jpg' alt="image">&lt;/p>
&lt;p>它实现的功能是：&lt;/p>
&lt;ul>
&lt;li>在stream 1上面执行kernel 1 和 kernel 3&lt;/li>
&lt;li>在stream 2上面执行kernel 2，但是必须等到stream 1上面的kernel 1执行结束之后才能开始&lt;/li>
&lt;/ul>
&lt;p>为此，我们在stream 1上面launch kernel 1之后，创建一个event来记录当前stream 1的状态（具体来说就是队列里有哪些kernel还没执行），然后把这个event放进stream 2的队列里，让stream 2执行一个想象中的“wait for event 1”的kernel，这个kernel直到kernel 1执行结束之后才离开队列。于是，在这个event之后加入队列的kernel 2，就必须等到kernel 1执行结束之后才能开始了。&lt;/p>
&lt;p>这样的好处在于，我们不仅控制了kernel 1和 kernel 2的执行顺序，而且把kernel 2和kernel 3并发执行了，节省了时间（蓝色区域为两个kernel同时执行的时间段）。&lt;/p>
&lt;h3 id="cuda-graph" class="heading-element">&lt;span>CUDA Graph&lt;/span>
 &lt;a href="#cuda-graph" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>对GPU进行性能优化时，cudagraph是绕不开的话题。不仅是GPU，大部分的xpu都会提供类似graph mode的优化，相比于每次分别由CPU进行kernel launch的eager mode，graph mode通常都会有较大性能提升，然而也经常容易出现各种各样的奇怪问题。&lt;/p>
&lt;div class="details admonition note open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-solid fa-pencil-alt" aria-hidden="true">&lt;/i>由于实习期间对CUDA Graph接触的比较多，实际上 CUDA Graph主要是为了减少 kernel launch 造成的开销，于是乎将多个琐碎的小kernel改为一次launch，具体的操作方式分为：一次Capture-&amp;gt;多次Replay&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;p>&lt;strong>graph capture期间禁止执行的函数&lt;/strong>&lt;/p>
&lt;p>当一个stream处在graph capture状态时，实际上CPU下发的kernel都没有执行，只是被记录下来了。因此，与GPU kernel执行状态相关的函数都不能使用，例如：&lt;/p>
&lt;ul>
&lt;li>对stream进行同步（cudaStreamSynchronize）&lt;/li>
&lt;li>隐含stream同步的操作（对context进行同步、对device进行同步，都会强制内部包含的全部stream进行同步，如果其中有stream处在graph capture状态，就会报错）&lt;/li>
&lt;li>隐含stream同步的操作,如当前graph capture的stream是blocking stream，则涉及null stream的操作都不可用，例如cudaMalloc&lt;/li>
&lt;li>对stream上面record的event进行的状态查询、同步操作&lt;/li>
&lt;li>&lt;mark class="mark-default">另外断点调试在这期间也是用不了的&lt;/mark>&lt;/li>
&lt;/ul>
&lt;p>更详细的限制可以看官方文档：&lt;a href="https://docs.pytorch.org/docs/stable/notes/cuda.html"target="_blank" rel="external nofollow noopener noreferrer">https://docs.pytorch.org/docs/stable/notes/cuda.html&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">contextlib&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">contextmanager&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@contextmanager&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">graph_capture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pool&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stream&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">capture_error_mode&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;global&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dump_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CUDAGraph&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dump_path&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">enable_debug_mode&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">graph&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cuda_graph&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">pool&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">pool&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">stream&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">stream&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">capture_error_mode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">capture_error_mode&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="n">g&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dump_path&lt;/span> &lt;span class="ow">is&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">debug_dump&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dump_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">ctypes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Load the CUDA runtime library&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudart&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CDLL&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;libcudart.so&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Define cudaMemcpyKind enumeration as in the CUDA API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyHostToHost&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyHostToDevice&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyDeviceToHost&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyDeviceToDevice&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyDefault&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Setup the prototype of the cudaMemcpyAsync function&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyAsync&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cudart&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cudaMemcpyAsync&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyAsync&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">argtypes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_void_p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># void* dst&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_void_p&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># const void* src&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_size_t&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># size_t count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># enum cudaMemcpyKind&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_void_p&lt;/span> &lt;span class="c1"># cudaStream_t stream&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cudaMemcpyAsync&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">restype&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ctypes&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">c_int&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Placeholder input used for capture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">zeros&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,),&lt;/span> &lt;span class="n">device&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cpu&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">static_a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pin_memory&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">zeros&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,),&lt;/span> &lt;span class="n">device&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cpu&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">static_b&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pin_memory&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">zeros&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">,),&lt;/span> &lt;span class="n">device&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cpu&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">static_output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pin_memory&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">compute&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">static_a&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cuda&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">non_blocking&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">static_b&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">to&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;cuda&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">non_blocking&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">cudaMemcpyAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">static_output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data_ptr&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data_ptr&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">numel&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">element_size&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">cudaMemcpyDeviceToHost&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">current_stream&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda_stream&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">result&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">static_output&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Warmup before capture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">s&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Stream&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">s&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">wait_stream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">current_stream&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">stream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compute&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">current_stream&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">wait_stream&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Captures the graph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># To allow capture, automatically sets a side stream as the current stream in the context&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">with&lt;/span> &lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">nvtx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;capture&amp;#34;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="n">graph_capture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dump_path&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;graph.dot&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compute&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Run the graph&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">g&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">replay&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">current_stream&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">synchronize&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">static_output&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_a&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">static_b&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">g&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">replay&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cuda&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">current_stream&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">synchronize&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">static_output&lt;/span>&lt;span class="p">)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>其中，因为PyTorch的功能的一些限制，我们的代码发生了变化：PyTorch里的 &lt;code>a.to(&amp;quot;cpu&amp;quot;)&lt;/code>，会强制同步使得host to device完成同步，即使加上 &lt;code>non_blocking=True&lt;/code> 也无法改变。这与cudagraph不兼容。为了解决这个问题，我们手动调用了 &lt;code>cudaMemcpyAsync&lt;/code> 函数，实现了异步拷贝到CPU的功能。（如果要PyTorch里直接实现这一功能，需要async CPU的支持。）&lt;/p>
&lt;p>执行以上代码，捕获的计算图为：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n2qraosuj31401r1gyi.jpg' alt="image">&lt;/p>
&lt;h2 id="ptx--sass--libdevice" class="heading-element">&lt;span>PTX &amp;amp; SASS &amp;amp; libdevice&lt;/span>
 &lt;a href="#ptx--sass--libdevice" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/21358921473"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/21358921473&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="ptxparallel-thread-execution" class="heading-element">&lt;span>PTX(Parallel Thread Execution)&lt;/span>
 &lt;a href="#ptxparallel-thread-execution" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul>
&lt;li>PTX 是NVIDIA设计的中间表示（IR），类似于GPU的汇编语言，但独立于具体硬件架构。&lt;/li>
&lt;li>PTX代码由NVCC生成，可以被NVIDIA的驱动程序进一步编译为特定GPU架构的机器代码（SASS）。&lt;/li>
&lt;li>PTX代码是文本格式，便于阅读和调试。&lt;/li>
&lt;/ul>
&lt;p>示例：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n2w08wp2j30wf0j83zl.jpg' alt="image">&lt;/p>
&lt;h3 id="sass-streaming-assembly" class="heading-element">&lt;span>SASS (Streaming ASSembly)&lt;/span>
 &lt;a href="#sass-streaming-assembly" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul>
&lt;li>SASS 是NVIDIA GPU的机器代码，直接由GPU硬件执行。&lt;/li>
&lt;li>SASS是二进制格式，特定于具体的GPU架构（如Ampere、Turing）。&lt;/li>
&lt;li>SASS代码通常通过反汇编工具（如cuobjdump）查看。&lt;/li>
&lt;/ul>
&lt;p>示例：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n2wezp5aj30w006fweo.jpg' alt="image">&lt;/p>
&lt;h3 id="libdevice" class="heading-element">&lt;span>libdevice&lt;/span>
 &lt;a href="#libdevice" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul>
&lt;li>libdevice 是NVIDIA提供的一个数学函数库，包含高度优化的设备端数学函数（如sin、exp、log等）&lt;/li>
&lt;li>这些函数以PTX或SASS形式提供，可以直接链接到CUDA程序中。&lt;/li>
&lt;li>libdevice库的文件通常命名为libdevice.*.bc（LLVM bitcode格式）。&lt;/li>
&lt;/ul>
&lt;p>在CUDA程序中，调用标准数学函数（如sin、cos）时，NVCC会自动链接libdevice库。开发者也可以通过 &lt;code>-lcudadevrt&lt;/code>显式链接libdevice。&lt;/p>
&lt;h3 id="转换流程" class="heading-element">&lt;span>转换流程&lt;/span>
 &lt;a href="#%e8%bd%ac%e6%8d%a2%e6%b5%81%e7%a8%8b" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>以下是CUDA代码从高级语言到最终机器代码的转换流程：&lt;/p>
&lt;ol>
&lt;li>CUDA C/C++ → PTX&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>NVCC将CUDA设备代码编译为PTX。&lt;/li>
&lt;li>示例命令：&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">nvcc -ptx vector_add.cu -o vector_add.ptx&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ol start="2">
&lt;li>PTX → SASS&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>NVIDIA的驱动程序将PTX代码编译为特定GPU架构的SASS代码。&lt;/li>
&lt;li>这一步在运行时或安装时完成。&lt;/li>
&lt;/ul>
&lt;h2 id="cutlass--cute" class="heading-element">&lt;span>CUTLASS &amp;amp; CUTE&lt;/span>
 &lt;a href="#cutlass--cute" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="cutlass" class="heading-element">&lt;span>CUTLASS&lt;/span>
 &lt;a href="#cutlass" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>CUTLASS（CUDA Template for Linear Algebra Subroutines）是指NVIDIA开源的一个高性能CUDA模板库，用于实现高效的线性代数计算，里面提供了包括访存，计算，流水线编排等多个level的模板。用户通过CUTLASS模板组装以及特化，可以搭建出自己需要的高性能CUDA Kernel。&lt;/p>
&lt;p>核心思想是模块化和通用化。它不像传统的库（如 cuBLAS）那样提供预编译好的函数，而是提供了一套模板，让你能够根据自己的需求组合出最优化的计算内核。这使得开发者可以针对特定的矩阵尺寸、数据类型（如 FP16、FP32、INT8 等）和计算模式，生成定制化的、性能极高的内核，而无需从头手写汇编代码。&lt;/p>
&lt;h3 id="cute" class="heading-element">&lt;span>CUTE&lt;/span>
 &lt;a href="#cute" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>CUTE 是一个更底层的、用于描述和操作 GPU 上多维数据布局 (tensor layout) 的 C++ 库。 它不是一个完整的应用库，而是一套强大的、用于构建其他高级库（如 CUTLASS）的工具集。&lt;/p>
&lt;p>CUTE 的核心思想是提供一套数学化的、声明式的张量抽象，让开发者可以精确地描述数据在内存中的排布方式。它解决了以下核心问题：&lt;/p>
&lt;p>张量布局 (Tensor Layout)： 如何描述一个多维数组（张量）在内存中的存储方式，例如行主序、列主序，或者更复杂的分块和交错布局。&lt;/p>
&lt;p>张量视图 (Tensor View)： 如何在不移动数据的情况下，创建张量的子视图，并对子视图进行操作。&lt;/p>
&lt;p>跨步操作 (Striding)： 如何高效地计算多维数组中元素的地址。&lt;/p>
&lt;p>你可以把 CUTE 理解为一个“张量布局的 DSL (Domain-Specific Language)”。 它提供了一种简洁的方式来表达复杂的数据访问模式，这对于编写 GPU 上的高性能通用代码至关重要。CUTLASS 内部就广泛使用了 CUTE 来管理和操作其张量数据，从而实现了其灵活性和性能。&lt;/p>
&lt;h2 id="global-memory的访存合并" class="heading-element">&lt;span>Global Memory的访存合并&lt;/span>
 &lt;a href="#global-memory%e7%9a%84%e8%ae%bf%e5%ad%98%e5%90%88%e5%b9%b6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;blockquote>
&lt;p>参考文献：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/675186810"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/675186810&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zhuanlan.zhihu.com/p/641639133"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/641639133&lt;/a>&lt;/li>
&lt;/ul>&lt;/blockquote>
&lt;p>当前的GPU架构允许通过编译选项来控制是否启用一级缓存。当一级缓存被禁用时，对全局内存的加载请求将直接进入二级缓存；如果二级缓存未命中，将由DRAM完成请求。核函数从全局内存DRAM中读取数据有两种粒度， 使用一级缓存时，每次按照128字节进行缓存；不使用一级缓存时，每次按照32字节进行缓存。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-shell" data-lang="shell">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 禁用一级缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-Xptxas -dlcm&lt;span class="o">=&lt;/span>cg
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 启用一级缓存&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">-Xptxas -dlcm&lt;span class="o">=&lt;/span>ca&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="传输延迟" class="heading-element">&lt;span>传输延迟&lt;/span>
 &lt;a href="#%e4%bc%a0%e8%be%93%e5%bb%b6%e8%bf%9f" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>在host端和device端之间存在latency，数据通过PCI-E总线从CPU传输给GPU，我们必须避免频繁的host、device间数据传输，即使是最新的PCIE 3.0 x16接口，其双向带宽也只有32GB/s&lt;/p>
&lt;p>在device内部也存在latency，即数据从gpu的存储器到multi-processor（SM）的传输。&lt;/p>
&lt;p>访问一次全局内存，将耗费400~600个cycle，成本是非常高的，所以必须谨慎对待全局内存的访问&lt;/p>
&lt;p>我们把一次内存请求——也就是从内核函数发起请求，到硬件响应返回数据这个过程称为一个内存事务（加载和存储都行）。&lt;/p>
&lt;h3 id="合并访存" class="heading-element">&lt;span>合并访存&lt;/span>
 &lt;a href="#%e5%90%88%e5%b9%b6%e8%ae%bf%e5%ad%98" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>数据从全局内存到SM（stream-multiprocessor）的传输，会进行cache，如果cache命中了，下一次的访问的耗时将大大减少。&lt;/p>
&lt;blockquote>
&lt;p>基本逻辑是： 首先判断这个 Kernel 的数据流路径，是否使用了 L1 cache，由此得出当前内存访问的最小粒度： 32 Bytes / 128 Bytes. 分析原始数据存储的结构，结合访存粒度，分析数据访问是否内存对齐，数据是否能合并访问。&lt;/p>&lt;/blockquote>
&lt;p>对于L1 cache，每次按照128字节进行缓存；对于L2 cache，每次按照32字节进行缓存。&lt;/p>
&lt;p>意思是表示线程束中每个线程以一个字节（&lt;code>1*32=32&lt;/code>）、16位（&lt;code>2*32=64&lt;/code>）、32位（&lt;code>4*32=128&lt;/code>）为单位读取数据。前提是，访问必须连续，并且访问的地址是以32字节对齐。&lt;/p>
&lt;p>例子，假设每个thread读取一个float变量，那么一个warp（32个thread）将会执行32*4=128字节的合并访存指令，通过一次访存操作完成所有thread的读取请求。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n6sp6qf1j30ri081t96.jpg' alt="image">&lt;/p>
&lt;p>对于L2 cache，合并访存的字节减少为32字节，那么L2 cache相对L1 cache的好处就是：&lt;/p>
&lt;p>&lt;mark class="mark-default">在非对齐访问、分散访问（非连续访问）的情况下，提高吞吐量（cache的带宽利用率）&lt;/mark>&lt;/p>
&lt;p>&lt;strong>对齐/非对齐访问&lt;/strong>&lt;/p>
&lt;p>当一个内存事务的首个访问地址是缓存粒度（32或128字节）的偶数倍的时候：比如二级缓存32字节的偶数倍64，128字节的偶数倍256的时候，这个时候被称为对齐内存访问，非对齐访问就是除上述的其他情况，非对齐的内存访问会造成带宽浪费。&lt;/p>
&lt;p>&lt;strong>合并/非合并访问&lt;/strong>&lt;/p>
&lt;p>当一个线程束内的线程访问的内存都在一个内存块（缓存粒度）里的时候，就会出现合并访问。&lt;/p>
&lt;p>对齐合并访问的状态是理想化的，也是最高速的访问方式，当线程束内的所有线程访问的数据在一个内存块，并且数据是从内存块的首地址开始被需要的，那么对齐合并访问出现了。为了最大化全局内存访问的理想状态，尽量将线程束访问内存组织成对齐合并的方式，这样的效率是最高的。&lt;/p>
&lt;p>一个线程束加载数据，使用一级缓存，并且这个事务所请求的所有数据在一个128字节的对齐的地址段上（对齐的地址段是我自己发明的名字，就是首地址是粒度的偶数倍，那么上面这句话的意思是，所有请求的数据在某个首地址是粒度偶数倍的后128个字节里），具体形式如下图，这里请求的数据是连续的，其实可以不连续，但是不要越界就好。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n5sr9oc7j30mm069glx.jpg' alt="image">&lt;/p>
&lt;p>如果一个事务加载的数据分布在不一个对齐的地址段上，就会有以下两种情况：&lt;/p>
&lt;ol>
&lt;li>连续的，但是不在一个对齐的段上，比如，请求访问的数据分布在内存地址 1-128，那么0-127和128-255这两段数据要传递两次到SM&lt;/li>
&lt;li>不连续的，也不在一个对齐的段上，比如，请求访问的数据分布在内存地址0-63和128-191上，明显这也需要两次加载。&lt;/li>
&lt;/ol>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3ly1i4n5t0v2sij30x209874z.jpg' alt="image">&lt;/p>
&lt;p>数据分散开了，thread0的请求在128之前，后面还有请求在256之后，所以需要三个内存事务，而利用率，也就是从主存取回来的数据被使用到的比例，只有 128/(3*128) 的比例。这个比例低会造成带宽的浪费，最极端的表现，就是如果每个线程的请求都在不同的段，也就是一个128字节的事务只有1个字节是有用的，那么利用率只有 1/128.&lt;/p>
&lt;p>&lt;strong>实例——写一个 内存访问非对齐的kernel&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">__global__&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">sumArraysGPU&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">A&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">B&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">C&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">tid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">blockIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">blockDim&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">threadIdx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tid&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">//添加偏移量 从 gld memory 读取数据带地址偏移 这样可以控制内存是否对齐
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">index&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">tid&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">index&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这里用一个 sum kernel 进行举例，&lt;code>float*A, float*, float*C&lt;/code> 都是从 glb mem 读取数据，每个 thread 会去load 一个数据进行处理。通过 offset 可以改变 thead load 数据的偏移地址，从而来试验不同 cache 粒度的 合并访问现象。&lt;/p>
&lt;p>可以发现禁用 L1 cache 前后，另外 offset = 0 和 offset = 32 得到上述理论结果。&lt;/p>
&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>另外，面临非对齐访问，使用 L2 cache能够相对获得较高的 cache 利用率，这也很好理解，因为 L2 cache 的内存块大小更小，即使非对齐，有效地址偏移位数少&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h2 id="常用的profile工具和方法" class="heading-element">&lt;span>常用的profile工具和方法&lt;/span>
 &lt;a href="#%e5%b8%b8%e7%94%a8%e7%9a%84profile%e5%b7%a5%e5%85%b7%e5%92%8c%e6%96%b9%e6%b3%95" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;ol>
&lt;li>Nsight System&lt;/li>
&lt;li>Nsight Compute&lt;/li>
&lt;li>compute-sanitizer&lt;/li>
&lt;li>&lt;a href="https://blog.vllm.ai/2025/08/11/cuda-debugging.html"target="_blank" rel="external nofollow noopener noreferrer">vllm core dump&lt;/a>&lt;/li>
&lt;li>perfetto 分析 torch profile 文件&lt;/li>
&lt;/ol></description></item><item><title>秋招timeline</title><link>https://yitaonote.com/2025/adc4087/</link><pubDate>Thu, 21 Aug 2025 21:44:59 +0800</pubDate><guid>https://yitaonote.com/2025/adc4087/</guid><category domain="https://yitaonote.com/categories/%E6%89%BE%E5%B7%A5%E4%BD%9C/">找工作</category><description>&lt;p>记录秋招找工作的投递 timeline&lt;/p>
&lt;h2 id="人才计划" class="heading-element">&lt;span>人才计划&lt;/span>
 &lt;a href="#%e4%ba%ba%e6%89%8d%e8%ae%a1%e5%88%92" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="-腾讯青云-pcg" class="heading-element">&lt;span>❌ 腾讯青云-PCG&lt;/span>
 &lt;a href="#-%e8%85%be%e8%ae%af%e9%9d%92%e4%ba%91-pcg" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-07-08&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-07-24&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-04&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-快star" class="heading-element">&lt;span>❌ 快STAR&lt;/span>
 &lt;a href="#-%e5%bf%abstar" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-04&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-07&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-百度aidu" class="heading-element">&lt;span>❌ 百度AIDU&lt;/span>
 &lt;a href="#-%e7%99%be%e5%ba%a6aidu" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-07-08&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-科大讯飞-飞星计划" class="heading-element">&lt;span>❌ 科大讯飞-飞星计划&lt;/span>
 &lt;a href="#-%e7%a7%91%e5%a4%a7%e8%ae%af%e9%a3%9e-%e9%a3%9e%e6%98%9f%e8%ae%a1%e5%88%92" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-20&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-小米人才计划" class="heading-element">&lt;span>❌ 小米人才计划&lt;/span>
 &lt;a href="#-%e5%b0%8f%e7%b1%b3%e4%ba%ba%e6%89%8d%e8%ae%a1%e5%88%92" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-26&lt;/div>&lt;/li>&lt;/ul>&lt;h2 id="提前批" class="heading-element">&lt;span>提前批&lt;/span>
 &lt;a href="#%e6%8f%90%e5%89%8d%e6%89%b9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="-滴滴提前批" class="heading-element">&lt;span>❌ 滴滴提前批&lt;/span>
 &lt;a href="#-%e6%bb%b4%e6%bb%b4%e6%8f%90%e5%89%8d%e6%89%b9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-07-16&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面（当时人在国外参会，没收到邮件，打电话来调整时间直接推到一周后，过段时间挂）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-07-31&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-拼多多提前批" class="heading-element">&lt;span>⏰ 拼多多提前批&lt;/span>
 &lt;a href="#-%e6%8b%bc%e5%a4%9a%e5%a4%9a%e6%8f%90%e5%89%8d%e6%89%b9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-03&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">测评&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-13&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-17&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-31&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-08&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 6;">&lt;div class="fi-timeline-item__content">三面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-20&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 7;">&lt;div class="fi-timeline-item__content">HR面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-31&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-minimax提前批" class="heading-element">&lt;span>❌ MiniMax提前批&lt;/span>
 &lt;a href="#-minimax%e6%8f%90%e5%89%8d%e6%89%b9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-05&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-28&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-17&lt;/div>&lt;/li>&lt;/ul>&lt;h2 id="正式批" class="heading-element">&lt;span>正式批&lt;/span>
 &lt;a href="#%e6%ad%a3%e5%bc%8f%e6%89%b9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="蚂蚁" class="heading-element">&lt;span>✅蚂蚁&lt;/span>
 &lt;a href="#%e8%9a%82%e8%9a%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">转正答辩&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-26&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">意向&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-06&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-商汤" class="heading-element">&lt;span>❌ 商汤&lt;/span>
 &lt;a href="#-%e5%95%86%e6%b1%a4" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-09&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-米哈游" class="heading-element">&lt;span>❌ 米哈游&lt;/span>
 &lt;a href="#-%e7%b1%b3%e5%93%88%e6%b8%b8" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-09&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-虾皮" class="heading-element">&lt;span>❌ 虾皮&lt;/span>
 &lt;a href="#-%e8%99%be%e7%9a%ae" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-21&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-猿辅导" class="heading-element">&lt;span>❌ 猿辅导&lt;/span>
 &lt;a href="#-%e7%8c%bf%e8%be%85%e5%af%bc" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-20&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-美团" class="heading-element">&lt;span>❌ 美团&lt;/span>
 &lt;a href="#-%e7%be%8e%e5%9b%a2" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-13&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-16&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">一面（在线推理团队）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-25&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">一面（强化学习框架团队）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-04&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">二面（强化学习框架团队）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-13&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-得物" class="heading-element">&lt;span>❌ 得物&lt;/span>
 &lt;a href="#-%e5%be%97%e7%89%a9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-12&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-腾讯" class="heading-element">&lt;span>❌ 腾讯&lt;/span>
 &lt;a href="#-%e8%85%be%e8%ae%af" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-19&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-元戎启行" class="heading-element">&lt;span>❌ 元戎启行&lt;/span>
 &lt;a href="#-%e5%85%83%e6%88%8e%e5%90%af%e8%a1%8c" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-20&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-10&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-快手" class="heading-element">&lt;span>❌ 快手&lt;/span>
 &lt;a href="#-%e5%bf%ab%e6%89%8b" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-23&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-09&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-19&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-b站" class="heading-element">&lt;span>✅ B站&lt;/span>
 &lt;a href="#-b%e7%ab%99" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-23&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-05&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-12&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">HR面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-19&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">意向&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-28&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-小红书" class="heading-element">&lt;span>❌ 小红书&lt;/span>
 &lt;a href="#-%e5%b0%8f%e7%ba%a2%e4%b9%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-24&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-21&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-阿里国际" class="heading-element">&lt;span>❌ 阿里国际&lt;/span>
 &lt;a href="#-%e9%98%bf%e9%87%8c%e5%9b%bd%e9%99%85" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-26&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-阿里淘天" class="heading-element">&lt;span>❌ 阿里淘天&lt;/span>
 &lt;a href="#-%e9%98%bf%e9%87%8c%e6%b7%98%e5%a4%a9" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-27&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-05&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-11&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">三面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-15&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">HR面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-19&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-阿里控股" class="heading-element">&lt;span>❌ 阿里控股&lt;/span>
 &lt;a href="#-%e9%98%bf%e9%87%8c%e6%8e%a7%e8%82%a1" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-27&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-滴滴" class="heading-element">&lt;span>❌ 滴滴&lt;/span>
 &lt;a href="#-%e6%bb%b4%e6%bb%b4" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-29&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-07&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-网易有道" class="heading-element">&lt;span>❌ 网易有道&lt;/span>
 &lt;a href="#-%e7%bd%91%e6%98%93%e6%9c%89%e9%81%93" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-29&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试（放弃）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-09&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-阶跃星辰" class="heading-element">&lt;span>❌ 阶跃星辰&lt;/span>
 &lt;a href="#-%e9%98%b6%e8%b7%83%e6%98%9f%e8%be%b0" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-08-29&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-22&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-25&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">三面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-14&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">CEO面（放弃）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-12-03&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-小米" class="heading-element">&lt;span>❌ 小米&lt;/span>
 &lt;a href="#-%e5%b0%8f%e7%b1%b3" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">测评&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-06&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-饿了么" class="heading-element">&lt;span>❌ 饿了么&lt;/span>
 &lt;a href="#-%e9%a5%bf%e4%ba%86%e4%b9%88" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-12&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-平头哥" class="heading-element">&lt;span>❌ 平头哥&lt;/span>
 &lt;a href="#-%e5%b9%b3%e5%a4%b4%e5%93%a5" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-百度" class="heading-element">&lt;span>❌ 百度&lt;/span>
 &lt;a href="#-%e7%99%be%e5%ba%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试（听说要提前实习，发实习offer，转正率40%，放弃）&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-19&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-字节跳动" class="heading-element">&lt;span>✅ 字节跳动&lt;/span>
 &lt;a href="#-%e5%ad%97%e8%8a%82%e8%b7%b3%e5%8a%a8" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-01&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-08&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-11&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">意向&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-22&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-上海ai-lab" class="heading-element">&lt;span>❌ 上海AI Lab&lt;/span>
 &lt;a href="#-%e4%b8%8a%e6%b5%b7ai-lab" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-02&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-小鹏汽车" class="heading-element">&lt;span>❌ 小鹏汽车&lt;/span>
 &lt;a href="#-%e5%b0%8f%e9%b9%8f%e6%b1%bd%e8%bd%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-04&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-理想汽车" class="heading-element">&lt;span>❌ 理想汽车&lt;/span>
 &lt;a href="#-%e7%90%86%e6%83%b3%e6%b1%bd%e8%bd%a6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-04&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-携程" class="heading-element">&lt;span>❌ 携程&lt;/span>
 &lt;a href="#-%e6%90%ba%e7%a8%8b" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-04&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">测评&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-06&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-18&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-京东" class="heading-element">&lt;span>✅ 京东&lt;/span>
 &lt;a href="#-%e4%ba%ac%e4%b8%9c" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-05&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-13&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">一面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-15&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 4;">&lt;div class="fi-timeline-item__content">二面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-23&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 5;">&lt;div class="fi-timeline-item__content">HR面&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-17&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 6;">&lt;div class="fi-timeline-item__content">意向&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-22&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-高德地图" class="heading-element">&lt;span>❌ 高德地图&lt;/span>
 &lt;a href="#-%e9%ab%98%e5%be%b7%e5%9c%b0%e5%9b%be" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-09&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-阿里云" class="heading-element">&lt;span>❌ 阿里云&lt;/span>
 &lt;a href="#-%e9%98%bf%e9%87%8c%e4%ba%91" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-10&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-蔚来" class="heading-element">&lt;span>❌ 蔚来&lt;/span>
 &lt;a href="#-%e8%94%9a%e6%9d%a5" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-10&lt;/div>&lt;/li>&lt;/ul>&lt;h3 id="-华为" class="heading-element">&lt;span>❌ 华为&lt;/span>
 &lt;a href="#-%e5%8d%8e%e4%b8%ba" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;ul
 class="fi-timeline"
 >&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 1;">&lt;div class="fi-timeline-item__content">投递&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-16&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 2;">&lt;div class="fi-timeline-item__content">笔试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-09-24&lt;/div>&lt;/li>&lt;li
 class="fi-timeline-item"
 data-size="medium"
 data-node="circle"
 style="--timeline-index: 3;">&lt;div class="fi-timeline-item__content">线下集中面试&lt;/div>
 &lt;div class="fi-timeline-item__timestamp is-bottom">2025-10-13&lt;/div>&lt;/li>&lt;/ul></description></item><item><title>分布式推理优化技术</title><link>https://yitaonote.com/2025/7ae2a3f/</link><pubDate>Tue, 19 Aug 2025 23:07:32 +0800</pubDate><guid>https://yitaonote.com/2025/7ae2a3f/</guid><category domain="https://yitaonote.com/categories/%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96/">推理优化</category><description>&lt;h2 id="分布式通信原语" class="heading-element">&lt;span>分布式通信原语&lt;/span>
 &lt;a href="#%e5%88%86%e5%b8%83%e5%bc%8f%e9%80%9a%e4%bf%a1%e5%8e%9f%e8%af%ad" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>在大模型分布式训练或推理时，常用到一些分布式通信原语，这里做一个简单总结&lt;/p>
&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://zhuanlan.zhihu.com/p/717814079"target="_blank" rel="external nofollow noopener noreferrer">https://zhuanlan.zhihu.com/p/717814079&lt;/a>&lt;/p>&lt;/blockquote>
&lt;h3 id="broadcast一对多" class="heading-element">&lt;span>Broadcast(一对多)&lt;/span>
 &lt;a href="#broadcast%e4%b8%80%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>在集合通信中，如果某个节点想把自身的数据发送到集群中的其他节点，那么就可以使用广播 Broadcast 的操作。Broadcast 代表广播行为，执行 Broadcast 时，数据从主节点 2 广播至其他各个指定的节点（0~3）。 Broadcast 操作是将某节点的输入广播到其他节点上，分布式机器学习中常用于网络参数的初始化。&lt;/p>
&lt;p>如图中，从单个 sender 数据发送到其他节点上，将 2 卡大小为 1xN 的 Tensor 进行广播， 最终每张卡输出均为[1xN]的矩阵。操作过后，所有的进程上数据一致。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgroafcdj30fy049jrt.jpg' alt="image">&lt;/p>
&lt;p>&lt;strong>应用场景&lt;/strong>&lt;/p>
&lt;p>Broadcast 将一张 GPU 卡上的数据同步到其他所有的 GPU 卡上，其应用场景有： 数据并行的初始化：1）确保每张卡上的初始参数是一致的；2）确保每张卡上模型相同。&lt;/p>
&lt;h3 id="scatter一对多" class="heading-element">&lt;span>Scatter(一对多)&lt;/span>
 &lt;a href="#scatter%e4%b8%80%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Scatter 操作表示一种散播行为，将主节点的数据进行划分并散布至其他指定的节点。Scatter 与 Broadcast 非常相似，都是一对多的通信方式，不同的是 Broadcast 的2号进程将相同的信息发送给所有的进程，而 Scatter 则是将数据的不同部分，按需发送给所有的进程。在分布式场景下，一般是将源进程上数据列表中的N个值按顺序分散到通讯组中的N个进程中。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgu2h7e9j30g604s74q.jpg' alt="image">&lt;/p>
&lt;p>&lt;strong>应用场景&lt;/strong>&lt;/p>
&lt;p>模型并行里初始化时将模型 scatter 到不同的 GPU 上；数据并行，将数据按卡数分发到不同的 GPU 上。&lt;/p>
&lt;h3 id="gather多对一" class="heading-element">&lt;span>Gather(多对一)&lt;/span>
 &lt;a href="#gather%e5%a4%9a%e5%af%b9%e4%b8%80" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Gather 操作将多个 sender 上的数据收集到单个节点上，Gather 可以理解为反向的 Scatter。 Gather 操作会从多个进程里面收集数据到一个进程上面。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgv7r740j30g204v74v.jpg' alt="image">&lt;/p>
&lt;h3 id="reduce多对一" class="heading-element">&lt;span>Reduce(多对一)&lt;/span>
 &lt;a href="#reduce%e5%a4%9a%e5%af%b9%e4%b8%80" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Reuduce 称为规约运算，是一系列简单运算操作的统称，细分可以包括：SUM、MIN、 MAX、PROD、LOR 等类型的规约操作。Reduce 从多个 sender 那里接收数据，最终 combine 到一个节点上。相比与all_reduce操作，不同点在于reduce在执行时需要指定目标进程，只有目标进程会收到最终的归约向量。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgxb4wnrj30g805h3z4.jpg' alt="image">&lt;/p>
&lt;p>&lt;strong>应用场景&lt;/strong>&lt;/p>
&lt;p>如数据并行时的梯度规约&lt;/p>
&lt;h3 id="all-gather多对多" class="heading-element">&lt;span>All Gather(多对多)&lt;/span>
 &lt;a href="#all-gather%e5%a4%9a%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>All Gather 会收集所有进程上的数据，并同步给所有进程。从最基础的角度来看，All Gather 相当于一个 Gather 操作之后跟着一个 Broadcast 操作。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgy7565xj30fy04cdge.jpg' alt="image">&lt;/p>
&lt;h3 id="allreduce多对多" class="heading-element">&lt;span>AllReduce(多对多)&lt;/span>
 &lt;a href="#allreduce%e5%a4%9a%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>Reduce 是一系列简单运算操作的统称，All Reduce 则是在所有的进程上都应用同样的 Reduce 操作。All Reduce 操作可通过单节点上 Reduce + Broadcast 操作完成。在 NCCL 中的 All Reduce 中，则是从多个 sender 那里接收数据，最终合并和分发到每一个节点上。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgyec03oj30gd04yq3l.jpg' alt="image">&lt;/p>
&lt;h3 id="reducescatter多对多" class="heading-element">&lt;span>ReduceScatter(多对多)&lt;/span>
 &lt;a href="#reducescatter%e5%a4%9a%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>归约分散操作，该操作行为上可以理解为在一个通信组内依次执行一个reduce操作和一个scatter操作，其示意图如下：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgyhpqy5j30g804974y.jpg' alt="image">&lt;/p>
&lt;p>首先，执行一个reduce操作（比如在0卡上），将每张卡上的1、2、3、4收集，那么0卡上得到4、8、12、16。然后执行一个scatter操作，将数据分割到不同卡上，每张卡的数据依次为：4、8、12、16。&lt;/p>
&lt;p>在大模型训练过程中，常常会执行all_reduce操作，确保每张卡的parameter相同，为了是实现并行化，一般会将all_reduce操作进行拆分，一般由一个reduce_scatter和一个all_gather组成。如下所示：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgz5710kj30z10j6474.jpg' alt="image">&lt;/p>
&lt;h3 id="alltoall多对多" class="heading-element">&lt;span>AllToAll(多对多)&lt;/span>
 &lt;a href="#alltoall%e5%a4%9a%e5%af%b9%e5%a4%9a" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>All to All 作为全交换操作，每个进程的接收缓冲区和发送缓冲区都是一个分为若干个数据块的数组。All to All 的具体操作是：将进程 i 的发送缓冲区中的第 j 块数据发送给进程 j，进程 j 将接收到的来 自进程 i 的数据块放在自身接收缓冲区的第 i 块位置。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgzgm619j30g3045aaq.jpg' alt="image">&lt;/p>
&lt;p>All to All 与 All Gather 区别：All Gather 操作中，不同进程向某一进程收集到的数据是完全 相同的，而在 All to All 中，不同的进程向某一进程收集到的数据是不同的。在每个进程的发送 缓冲区中，为每个进程都单独准备了一块数据。&lt;/p>
&lt;p>全体到全体操作，该操作需要一个通信组中的N个进程各自提供一个含有N个元素的列表，对N个元素分别执行一次all_gather和scatter操作，如下所示：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4lgzn4ekjj30v80boq6o.jpg' alt="image">&lt;/p>
&lt;h2 id="一些常见问题" class="heading-element">&lt;span>一些常见问题&lt;/span>
 &lt;a href="#%e4%b8%80%e4%ba%9b%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="为什么不缓存q而要缓存kv" class="heading-element">&lt;span>为什么不缓存Q，而要缓存KV？&lt;/span>
 &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e7%bc%93%e5%ad%98q%e8%80%8c%e8%a6%81%e7%bc%93%e5%ad%98kv" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;blockquote>
&lt;p>参考文献：&lt;a href="https://www.zhihu.com/question/653658936"target="_blank" rel="external nofollow noopener noreferrer">https://www.zhihu.com/question/653658936&lt;/a>&lt;/p>&lt;/blockquote>
&lt;p>因为分析attention计算可以知道，预测下一个token只用到了当前的query信息，而没有用到之前的query信息，所以不用缓存Q；
在序列的t位置，Q只有当前位置的，参与了计算，而K和V多个位置参与了计算，所以需要KV Cache，而不需要Q Cache。&lt;/p>
&lt;h3 id="为什么-deepseek-使用-fp8-而-qwen-使用-bf16" class="heading-element">&lt;span>为什么 DeepSeek 使用 FP8 而 Qwen 使用 BF16&lt;/span>
 &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-deepseek-%e4%bd%bf%e7%94%a8-fp8-%e8%80%8c-qwen-%e4%bd%bf%e7%94%a8-bf16" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;h3 id="为什么-cpu-需要三层-cache-而-gpu-只需要两层" class="heading-element">&lt;span>为什么 CPU 需要三层 Cache 而 GPU 只需要两层；&lt;/span>
 &lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-cpu-%e9%9c%80%e8%a6%81%e4%b8%89%e5%b1%82-cache-%e8%80%8c-gpu-%e5%8f%aa%e9%9c%80%e8%a6%81%e4%b8%a4%e5%b1%82" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>CPU三级缓存介绍：https://blog.csdn.net/weixin_43719763/article/details/128602160&lt;/p>
&lt;p>CPU三级缓存主要为了用于不同级别的数据访问，L1/L2是私有的，而L3是共享的，这样可以减少核心之间的数据竞争，降低数据访问延迟。而GPU由于线程数量较多，每个线程对缓存的压力较小，无需为单个线程进行优化缓存层级。而且GPU数据重用集中在相邻线程（如卷积核滑动窗口），L1/L2 缓存+共享内存即可满足。&lt;/p>
&lt;h3 id="分布式并行方法" class="heading-element">&lt;span>分布式并行方法&lt;/span>
 &lt;a href="#%e5%88%86%e5%b8%83%e5%bc%8f%e5%b9%b6%e8%a1%8c%e6%96%b9%e6%b3%95" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;blockquote>
&lt;p>数据并行（DP）、张量并行（TP）、流水线并行（PP）、上下文并行（CP）、专家并行（EP）以及序列并行（SP）。&lt;/p>&lt;/blockquote>
&lt;p>&lt;strong>为什么分布式并行很重要？&lt;/strong>&lt;/p>
&lt;p>一个70B参数的FP16模型，仅模型权重就需要约140GB的存储空间，远超任何单张主流GPU的显存容量。此外，训练过程中产生的优化器状态、梯度和中间激活值，更是将内存需求推向了TB级别&lt;/p>
&lt;p>训练如此大规模的模型需要巨大的计算资源，单一设备的计算能力根本无法满足需求。通过分布式并行，可以将计算任务分解到多个设备上，实现高效的并行计算。&lt;/p>
&lt;h4 id="基础并行策略dptppp" class="heading-element">&lt;span>基础并行策略（DP、TP、PP）&lt;/span>
 &lt;a href="#%e5%9f%ba%e7%a1%80%e5%b9%b6%e8%a1%8c%e7%ad%96%e7%95%a5dptppp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;h5 id="dp-data-parallelism-dp" class="heading-element">&lt;span>DP (Data Parallelism, DP)&lt;/span>
 &lt;a href="#dp-data-parallelism-dp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>核心原理：数据并行是最直观、最常用的并行方式。其核心思想是“模型复制，数据分片”。即将完整的模型副本放置在每一个计算设备（如GPU）上，然后将一个大的训练批次（mini-batch）分割成多个更小的微批次（micro-batch），每个设备独立处理一个微批次的数据。&lt;/p>
&lt;p>工作流程：&lt;/p>
&lt;ul>
&lt;li>分发： 将一个全局批次的数据切分，分发给各个GPU。&lt;/li>
&lt;li>前向传播： 每个GPU使用其上的模型副本独立计算其微批次数据的前向传播，得到损失。&lt;/li>
&lt;li>反向传播： 每个GPU独立计算梯度。梯度同步： 这是DP的关键步骤。所有GPU上的梯度需要通过一个集合通信操作（通常是All-Reduce）进行聚合（如求和或求平均），以确保每个GPU最终都得到全局梯度 。&lt;/li>
&lt;li>权重更新： 每个GPU使用同步后的全局梯度独立更新其本地的模型副本，从而保证所有副本在下一步开始前保持一致。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>这里涉及到本文的第一个“分布式通信”原语，全归约（All-Reduce），它处理 GPU 实例和节点之间的同步和通信。其实很好理解：就是每一个GPU把数据发送给其余GPU，每一个GPU对于接收到的数据进行求和。 Reduce（归约）：将多个GPU的数据发送到指定的GPU然后求和。&lt;/p>&lt;/blockquote>
&lt;p>DP方法的优缺点：&lt;/p>
&lt;p>优点：&lt;/p>
&lt;ul>
&lt;li>实现简单，易于集成到现有训练框架中&lt;/li>
&lt;li>适用于大多数标准模型架构&lt;/li>
&lt;li>扩展性较好，可轻松扩展到多GPU集群&lt;/li>
&lt;/ul>
&lt;p>缺点：&lt;/p>
&lt;ul>
&lt;li>内存瓶颈显著，无法解决&amp;quot;模型太大，单卡放不下&amp;quot;的问题&lt;/li>
&lt;li>随着GPU数量增加，All-Reduce操作的通信开销成为主要瓶颈&lt;/li>
&lt;li>扩展效率受限于通信带宽和延迟&lt;/li>
&lt;/ul>
&lt;p>关键优化：ZeRO (Zero Redundancy Optimizer)&lt;/p>
&lt;p>为了克服DP的内存冗余问题，ZeRO[1]技术应运而生。它通过在不同设备间分割和管理模型状态（而不仅仅是数据）来极大地优化内存使用。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j6cnz3zdj314007xwfc.jpg' alt="image">&lt;/p>
&lt;p>ZeRO-DP使得数据并行也能用于训练单卡无法容纳的大模型，是当前大规模训练框架（如DeepSpeed[2]）的核心技术之一。&lt;/p>
&lt;h5 id="tp-tensor-parallelism-tp" class="heading-element">&lt;span>TP (Tensor Parallelism, TP)&lt;/span>
 &lt;a href="#tp-tensor-parallelism-tp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>在使用 ZeRO 对模型的参数、梯度和优化器状态进行了分片，但当激活内存超过内存预算时，遇到了瓶颈。张量并行（TP），这是一种分片权重、梯度和优化器状态以及激活的方法——而且无需在计算之前将它们全部收集起来。&lt;/p>
&lt;p>张量并行利用了矩阵乘法的数学特性，A×B 。&lt;/p>
$$
\begin{aligned}
 A\cdot B &amp;= A\cdot [B_{1}, B_{2} \cdots] = [AB_{1}, AB_{2} \cdots] \\\\
 A\cdot B &amp;= [A_{1}, A_{2} \cdots] \begin{bmatrix}
B_{1} \\
B_{2} \\
\vdots
\end{bmatrix}
= \sum_{i=1}^{n}A_{i}B_{i}
\end{aligned}
$$&lt;p>这意味着可以通过单独计算 B 的每一列或单独计算每一行并将结果组合起来来进行矩阵乘法。示例如下：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j6tem6ujj30fa0bndg5.jpg' alt="image">&lt;/p>
&lt;p>在张量并行中，张量会沿着特定维度被分割成 N 个片段，并分布到 N 个 GPU 上。矩阵可以在列或行上进行分割，从而实现行或列并行。&lt;/p>
&lt;p>&lt;strong>第一个选项是使用按列（也称为按列线性）分片&lt;/strong>：将完整输入矩阵复制到每个工作节点，需要一种称为广播（broadcast）的操作，并将权重矩阵按列拆分。然后输入与部分权重矩阵相乘，最后使用全聚合（all-gather）操作合并结果。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j6uhusssj31400pdtap.jpg' alt="image">&lt;/p>
&lt;blockquote>
&lt;p>“分布式通信”原语：
广播（broadcast）：将一个GPU上的数据同时发送到所有其他GPU。
全聚合（all-gather）：每一个GPU把数据发送到其余所有GPU，每个GPU对于接受到的数据拼接为完整数据。&lt;/p>&lt;/blockquote>
&lt;p>&lt;strong>第二种选项称为按行（或行线性）分片&lt;/strong>：行线性意味着将权重矩阵按行分割成块。因此，需要使用散播（scatter）操作（第四种分布式通信原语！）而不是列线性分片中所使用的广播操作。每个工作节点上的结果已经处于正确的形状，但需要求和以得到最终结果，因此这种场景也需要一个全归约操作：&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j6vkskdvj31400ocac0.jpg' alt="image">&lt;/p>
&lt;blockquote>
&lt;p>“分布式通信”原语：
散播（scatter）：把一个GPU上的数据切分为多块，按照顺序向其他GPU发送数据块。&lt;/p>&lt;/blockquote>
&lt;p>核心原理： 张量并行，又称模型内部并行（Intra-layer Model Parallelism），旨在解决单层网络计算量过大或参数过多无法在单个设备上处理的问题。它将模型中的单个张量（如权重矩阵）沿特定维度切分到多个GPU上，并在这些GPU上协同完成运算。&lt;/p>
&lt;p>工作流程（以Transformer中的MLP层为例）：&lt;/p>
&lt;p>一个标准的MLP层包含两次线性变换。假设我们将模型并行到N个GPU上：&lt;/p>
&lt;ol>
&lt;li>第一个线性层的权重矩阵A可以按列切分成 [A1, A2, &amp;hellip;, AN]。输入X与每个Ai相乘得到Xi * Ai。这是一个并行的矩阵乘法，无需通信。&lt;/li>
&lt;li>第二个线性层的权重矩阵B可以按行切分成 [B1; B2; &amp;hellip;, BN]。在进行第二次矩阵乘法前，需要将第一步的输出结果 [Y1, Y2, &amp;hellip;, YN] 通过All-Gather操作在所有GPU间聚合，得到完整的Y。然后每个GPU计算Y * Bi。&lt;/li>
&lt;li>最后，将各个GPU上的结果通过Reduce-Scatter或简单的加和操作得到最终输出。&lt;/li>
&lt;/ol>
&lt;p>Megatron-LM[3]框架是张量并行的杰出代表，它巧妙地设计了Transformer中注意力层和MLP层的并行计算方式，将通信操作与计算有效结合。&lt;/p>
&lt;p>优缺点分析&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">优点&lt;/th>
 &lt;th style="text-align: center">缺点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">（1）直接减少了单个GPU上的内存占用（包括模型参数和激活值）（2）使得训练远超单卡内存限制的模型成为可能（3）计算粒度细，可以更好地利用GPU的并行计算能力&lt;/td>
 &lt;td style="text-align: center">（1）通信开销巨大且频繁，通常仅限于在单个计算节点内部进行高效扩展（2）对设备间的通信带宽要求极高（3）实现复杂度较高，需要仔细设计通信模式&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h5 id="流水线并行-pipeline-parallelism-pp" class="heading-element">&lt;span>流水线并行 (Pipeline Parallelism, PP)&lt;/span>
 &lt;a href="#%e6%b5%81%e6%b0%b4%e7%ba%bf%e5%b9%b6%e8%a1%8c-pipeline-parallelism-pp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>核心原理：流水线并行，又称层间模型并行（Inter-layer Model Parallelism），将模型的不同层（或层块）分配到不同的GPU上，形成一个计算&amp;quot;流水线&amp;quot;。数据在一个设备上完成部分层的计算后，其输出（即激活值）被传递到下一个设备，继续后续层的计算。&lt;/p>
&lt;p>工作流程：&lt;/p>
&lt;ol>
&lt;li>朴素流水线： 最简单的方式是将一个批次的数据依次通过所有GPU。但这会导致严重的“流水线气泡”（pipeline bubble），即在任何时刻，只有一个GPU在工作，其他GPU都在等待，利用率极低。&lt;/li>
&lt;li>微批次流水线 (Micro-batching)： 为了解决气泡问题，GPipe[4]等框架引入了微批次的概念 。它将一个大的批次（mini-batch）再切分成多个微批次（micro-batches），并将这些微批次依次送入流水线。这样，当第一个微批次在第二个GPU上计算时，第二个微批次就可以在第一个GPU上开始计算，实现了计算的重叠，从而显著减少了GPU的空闲时间。&lt;/li>
&lt;/ol>
&lt;p>优缺点分析&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">优点&lt;/th>
 &lt;th style="text-align: center">缺点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">（1）同样能有效降低单个GPU的内存占用（2）通信开销相对较低，因为通信只发生在流水线中相邻的两个阶段之间（3）通信内容主要是激活值，而非整个模型参数（4）实现相对简单，易于调试&lt;/td>
 &lt;td style="text-align: center">（1）仍然存在无法完全消除的&amp;quot;气泡&amp;quot;问题，特别是在流水线的启动和排空阶段（2）要求对模型进行合理的切分，以实现各阶段的负载均衡（3）&amp;ldquo;木桶效应&amp;quot;会限制整体性能，最慢的阶段决定整体吞吐量（4）流水线深度增加会导致额外的启动和结束延迟&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h4 id="长序列并行策略序列上下文并行" class="heading-element">&lt;span>长序列并行策略：序列、上下文并行&lt;/span>
 &lt;a href="#%e9%95%bf%e5%ba%8f%e5%88%97%e5%b9%b6%e8%a1%8c%e7%ad%96%e7%95%a5%e5%ba%8f%e5%88%97%e4%b8%8a%e4%b8%8b%e6%96%87%e5%b9%b6%e8%a1%8c" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>随着模型对长文本处理能力的需求日益增长，输入序列长度成为新的内存瓶颈。传统的TP和PP主要解决参数存储问题，而SP和CP则专注于解决因序列过长导致的激活值内存爆炸问题。&lt;/p>
&lt;h5 id="序列并行-sequence-parallelism-sp" class="heading-element">&lt;span>序列并行 (Sequence Parallelism, SP)&lt;/span>
 &lt;a href="#%e5%ba%8f%e5%88%97%e5%b9%b6%e8%a1%8c-sequence-parallelism-sp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>核心原理： Transformer架构中的自注意力机制（Self-Attention）需要在序列维度上进行全局计算，这使得沿序列维度进行并行化变得困难。序列并行巧妙地绕过了这一点，它选择性地对那些在序列维度上计算独立的模块（如LayerNorm、Dropout和MLP中的逐点操作）的输入激活进行切分。[5]&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j774cbv8j30ti0am75e.jpg' alt="image">&lt;/p>
&lt;p>工作流程：&lt;/p>
&lt;p>在进入非并行化的模块（如自注意力）之前，被切分的激活需要通过All-Gather操作在序列维度上拼接回完整的张量。
在完成该模块的计算后，输出的激活可以再次被切分，或者通过Reduce-Scatter操作将计算（如梯度累加）分散到各个设备上。&lt;/p>
&lt;p>价值与定位&lt;/p>
&lt;p>SP是TP的一个重要补充。TP虽然切分了权重，但每个GPU上仍然需要存储完整的激活张量，这在长序列场景下会成为内存瓶颈。SP通过切分激活，进一步降低了内存占用，使得在有限的显存下能够训练更长的序列或使用更大的批次大小。截至2024年的研究表明：将SP与PP结合（如Mnemosyne 2D并行化[6]）可以在长上下文预填充处理中达到80%以上的扩展效率。&lt;/p>
&lt;p>应用场景&lt;/p>
&lt;ol>
&lt;li>长文本处理：对于需要长文档、代码或对话的历史记录等场景，SP可以显著降低内存占用，支持更长的上下文窗口&lt;/li>
&lt;li>批量处理：在需要同时处理多个独立任务时，SP可以提高GPU利用率，减少空闲时间&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>“分布式通信”原语： 散播归约（Reduce-Scatter）：先做Scatter再做Reduce，先将GPU的数据分割为多个数据块，然后每一块数据按GPU序列进行散播Scatter。对所有的GPU都进行散射之后，当前GPU所接受到的所有数据块进行求和，也就是Reduce归约。&lt;/p>&lt;/blockquote>
&lt;h5 id="上下文并行-context-parallelism-cp" class="heading-element">&lt;span>上下文并行 (Context Parallelism, CP)&lt;/span>
 &lt;a href="#%e4%b8%8a%e4%b8%8b%e6%96%87%e5%b9%b6%e8%a1%8c-context-parallelism-cp" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>核心原理： 上下文并行[7]是处理超长序列的一种更激进的策略。与SP只切分部分激活不同，CP将网络输入和所有中间激活都沿序列维度进行划分。&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.ipfsscan.io/weibo/large/005wRZF3gy1i4j7bok2gij31400hvjtc.jpg' alt="image">&lt;/p>
&lt;p>工作流程（以注意力机制为例）：&lt;/p>
&lt;p>CP的实现方式如下：&lt;/p>
&lt;ol>
&lt;li>输入切分： 将长度为S的输入序列切分为N份，每个GPU处理长度为S/N的子序列。&lt;/li>
&lt;li>本地Q, K, V计算： 每个GPU根据自己的子序列计算出局部的Q, K, V张量。&lt;/li>
&lt;li>全局K, V同步： 在进行注意力计算之前，所有GPU需要通过一次All-Gather通信，将其本地的K和V张量广播给所有其他GPU。这样，每个GPU就拥有了完整的K和V张量，但只拥有局部的Q张量。&lt;/li>
&lt;li>注意力计算： 每个GPU使用其局部的Q和全局的K, V来计算注意力得分和输出，这部分计算是并行的。&lt;/li>
&lt;li>反向传播： 梯度计算也遵循类似的通信模式，通常涉及Reduce-Scatter操作。&lt;/li>
&lt;/ol>
&lt;p>价值与定位：&lt;/p>
&lt;p>CP的主要目标是最大程度地减少激活值内存占用，从而支持数万甚至数十万长度的上下文窗口。Amazon SageMaker 和 Megatron Core 等框架已经集成了CP功能 。其代价是在注意力层内部引入了额外的通信开销，但对于那些因激活内存而无法运行的超长序列场景，CP是必不可少的解决方案。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">优点&lt;/th>
 &lt;th style="text-align: center">缺点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">（1）极大降低内存占用（2）适用于需要处理数十万tokens的场景（3）可以与其他并行策略结合使用&lt;/td>
 &lt;td style="text-align: center">（1）引入额外的通信开销，尤其在注意力层（2）实现复杂度高，需要特殊硬件支持（3）可能影响计算效率，增加延迟&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h5 id="专家并行-expert-parallelism-ep" class="heading-element">&lt;span>专家并行 (Expert Parallelism, EP)&lt;/span>
 &lt;a href="#%e4%b8%93%e5%ae%b6%e5%b9%b6%e8%a1%8c-expert-parallelism-ep" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h5>&lt;p>核心原理： MoE架构用一组稀疏激活的“专家”网络（通常是FFN层）替换了传统Transformer中的密集FFN层。对于每个输入的token，一个可学习的“门控网络”（Gating Network）或“路由器”（Router）会动态地选择一小部分（如Top-2）专家来处理它 。这样，模型的总参数量可以非常巨大（所有专家参数之和），但每个token的实际计算量（FLOPs）却保持不变或仅少量增加。&lt;/p>
&lt;p>专家并行 (EP) 实现：&lt;/p>
&lt;p>EP是为MoE模型量身定制的并行策略。其核心思想是将不同的专家分配到不同的GPU上。&lt;/p>
&lt;ol>
&lt;li>专家分发： 将E个专家平均分配到N个GPU上，每个GPU拥有E/N个专家。&lt;/li>
&lt;li>路由计算： 所有GPU上的token首先通过门控网络，计算出各自应该被发送到哪些专家。&lt;/li>
&lt;li>全局通信 (All-to-All)： 这是EP中最关键也最昂贵的步骤。所有GPU参与一次All-to-All通信，将每个token发送到其目标专家所在的GPU。&lt;/li>
&lt;li>专家计算： 每个GPU用其本地的专家处理接收到的token。&lt;/li>
&lt;li>结果返回 (第二次All-to-All)： 计算完成后，再次通过All-to-All通信将处理结果发送回token原始的GPU。&lt;/li>
&lt;/ol>
&lt;p>优缺点分析&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th style="text-align: center">优点&lt;/th>
 &lt;th style="text-align: center">缺点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td style="text-align: center">（1）实现计算成本与模型参数量的解耦（2）是目前扩展模型至万亿参数规模的最有效路径（3）支持模型参数量与计算量分离扩展（4）可以通过增加专家数量而非计算量来扩展模型&lt;/td>
 &lt;td style="text-align: center">（1）高通信开销：两次All-to-All通信对网络带宽要求极高（2）负载不均：路由策略可能导致某些专家过载而另一些空闲（3）训练不稳定：路由器的学习过程可能很脆弱，导致训练崩溃（4）实现复杂度高，需要特殊硬件和软件支持&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h4 id="综合策略与性能基准" class="heading-element">&lt;span>综合策略与性能基准&lt;/span>
 &lt;a href="#%e7%bb%bc%e5%90%88%e7%ad%96%e7%95%a5%e4%b8%8e%e6%80%a7%e8%83%bd%e5%9f%ba%e5%87%86" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;p>在实践中，训练一个顶尖的大模型（如70B以上）绝不会只使用一种并行策略，而是将多种策略组合起来，形成所谓的3D或4D并行。&lt;/p>
&lt;p>常见组合范式：&lt;/p>
&lt;p>一个典型的4D并行配置可能是：&lt;/p>
&lt;ul>
&lt;li>张量并行 (TP)： 在单台服务器内部的GPU之间使用，以利用高速的NVLink互联。&lt;/li>
&lt;li>流水线并行 (PP)： 在多台服务器之间使用，以切分模型的深度。&lt;/li>
&lt;li>数据并行 (DP)： 在整个集群的所有设备上应用，以扩大总批次大小，加速收敛。&lt;/li>
&lt;li>专家/序列/上下文并行 (EP/SP/CP)： 根据模型架构（是否为MoE）和应用场景（是否为长上下文）选择性地加入，作为第四个维度。&lt;/li>
&lt;/ul></description></item><item><title>Kruskal算法求最小生成树</title><link>https://yitaonote.com/2025/acc2381/</link><pubDate>Sat, 16 Aug 2025 17:33:59 +0800</pubDate><guid>https://yitaonote.com/2025/acc2381/</guid><category domain="https://yitaonote.com/categories/%E7%AE%97%E6%B3%95/">算法</category><description>&lt;blockquote>
&lt;p>Kruskal 算法原理及证明见&lt;a href="https://www.acwing.com/activity/content/code/content/1745411/"target="_blank" rel="external nofollow noopener noreferrer">提高课&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;h2 id="kruskal算法求最小生成树" class="heading-element">&lt;span>Kruskal算法求最小生成树 $O(m\log m)$&lt;/span>
 &lt;a href="#kruskal%e7%ae%97%e6%b3%95%e6%b1%82%e6%9c%80%e5%b0%8f%e7%94%9f%e6%88%90%e6%a0%91" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;p>&lt;img loading="lazy" src='https://cdn.acwing.com/media/article/image/2021/06/29/94631_ff56a31cd8-IMG_20210629_141909_edit_223503600585686.jpg' alt="IMG_20210629_141909_edit_223503600585686.jpg">&lt;/p>
&lt;p>&lt;strong>这里重载运算符的方式也可以通过写cmp()来作为sort的第三个参数&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">bool&lt;/span> &lt;span class="nf">cmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Edge&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Edge&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">w&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="完整代码" class="heading-element">&lt;span>完整代码&lt;/span>
 &lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;algorithm&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstring&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100010&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">M&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">200010&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">INF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x3f3f3f3f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Edge&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">bool&lt;/span> &lt;span class="k">operator&lt;/span>&lt;span class="o">&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Edge&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">W&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="k">const&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">w&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">W&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;span class="n">edges&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">M&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">kruskal&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sort&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">edges&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">edges&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span> &lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cnt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">edges&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">edges&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">edges&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">p&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">res&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cnt&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">cnt&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cin&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">edges&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">kruskal&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;impossible&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Prim算法求最小生成树</title><link>https://yitaonote.com/2025/1f7d467/</link><pubDate>Sat, 16 Aug 2025 17:33:53 +0800</pubDate><guid>https://yitaonote.com/2025/1f7d467/</guid><category domain="https://yitaonote.com/categories/%E7%AE%97%E6%B3%95/">算法</category><description>&lt;blockquote>
&lt;p>Prim 算法原理及证明见&lt;a href="https://www.acwing.com/activity/content/code/content/1745411/"target="_blank" rel="external nofollow noopener noreferrer">提高课&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;p>Prim算法求最小生成树 $O(n^{2})$，跟Dijkstra很像&lt;/p>
&lt;p>&lt;img loading="lazy" src='https://cdn.acwing.com/media/article/image/2021/06/28/94631_240b2fb5d8-IMG_20210628_204051_edit_188738021723283.jpg' alt="IMG_20210628_204051_edit_188738021723283.jpg">&lt;/p>
&lt;h3 id="完整代码" class="heading-element">&lt;span>完整代码&lt;/span>
 &lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-c++" data-lang="c++">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;algorithm&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstring&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">510&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">INF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mh">0x3f3f3f3f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 当前点距离集合的距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">prim&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x3f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 最小生成树中所有边长度之和
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span> &lt;span class="c1">// 每次找到集合外的，距集合距离最小的点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">{&lt;/span> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// t = -1 表示当前还没还有找到任何一个点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 当前图是不连通的，不存在最小生成树
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// dist[t] 表示一条树边，加到生成树中去
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x3f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 无向图，处理重边
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prim&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;impossible&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Floyd求最短路</title><link>https://yitaonote.com/2025/6278ffa/</link><pubDate>Sat, 16 Aug 2025 17:33:47 +0800</pubDate><guid>https://yitaonote.com/2025/6278ffa/</guid><category domain="https://yitaonote.com/categories/%E7%AE%97%E6%B3%95/">算法</category><description>&lt;h2 id="floyd-算法" class="heading-element">&lt;span>Floyd 算法&lt;/span>
 &lt;a href="#floyd-%e7%ae%97%e6%b3%95" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="算法框架" class="heading-element">&lt;span>算法框架&lt;/span>
 &lt;a href="#%e7%ae%97%e6%b3%95%e6%a1%86%e6%9e%b6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>&lt;img loading="lazy" src='https://cdn.acwing.com/media/article/image/2021/06/28/94631_603f3714d7-IMG_20210628_164616_edit_183458334133463.jpg' alt="IMG_20210628_164616_edit_183458334133463.jpg">&lt;/p>
&lt;h3 id="适用场景" class="heading-element">&lt;span>适用场景&lt;/span>
 &lt;a href="#%e9%80%82%e7%94%a8%e5%9c%ba%e6%99%af" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>多源汇最短路，可以有负权边，但是不能有负权回路。&lt;/p>
&lt;h3 id="算法原理概述" class="heading-element">&lt;span>算法原理概述&lt;/span>
 &lt;a href="#%e7%ae%97%e6%b3%95%e5%8e%9f%e7%90%86%e6%a6%82%e8%bf%b0" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>基于动态规划。&lt;/p>
&lt;p>闫氏DP分析法：&lt;/p>
&lt;p>状态表示&lt;/p>
&lt;blockquote>
&lt;p>$d(k,i,j)$：从点 $i$ 出发，只经过 $1\sim k$ 这些中间点到达 $j$ 的最短距离。&lt;/p>&lt;/blockquote>
&lt;p>状态计算&lt;/p>
&lt;blockquote>
&lt;p>$d(k,i,j) = d(k-1,i,k) + d(k-1,k,j) \Rightarrow d(i,j) = d(i,k) + d(k,j)$&lt;/p>&lt;/blockquote>
&lt;h3 id="时间复杂度分析" class="heading-element">&lt;span>时间复杂度分析&lt;/span>
 &lt;a href="#%e6%97%b6%e9%97%b4%e5%a4%8d%e6%9d%82%e5%ba%a6%e5%88%86%e6%9e%90" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>三重循环，故复杂度为 $O(n^{3})$。&lt;/p>
&lt;h3 id="完整代码" class="heading-element">&lt;span>完整代码&lt;/span>
 &lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>注：一定要先循环&lt;code>k&lt;/code>，&lt;code>i&lt;/code>和&lt;code>j&lt;/code>的顺序可以任意颠倒。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstring&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;algorithm&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">210&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">INF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1e9&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Q&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="c1">// 邻接矩阵，也是floyd算法处理的距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">floyd&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">k&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">Q&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 去掉自环
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">INF&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">w&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 若有重边，则只保留最短的边
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">floyd&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Q&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 和bellman ford类似，即使终点与起点不连通，也还是可能会被负权邻边更新，所以适当放宽条件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">INF&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">puts&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;impossible&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Dijkstra求最短路II</title><link>https://yitaonote.com/2025/652842d/</link><pubDate>Sat, 16 Aug 2025 17:33:25 +0800</pubDate><guid>https://yitaonote.com/2025/652842d/</guid><category domain="https://yitaonote.com/categories/%E7%AE%97%E6%B3%95/">算法</category><description>&lt;h2 id="堆优化-dijkstra" class="heading-element">&lt;span>堆优化 Dijkstra&lt;/span>
 &lt;a href="#%e5%a0%86%e4%bc%98%e5%8c%96-dijkstra" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="算法框架--步骤" class="heading-element">&lt;span>算法框架 &amp;amp; 步骤&lt;/span>
 &lt;a href="#%e7%ae%97%e6%b3%95%e6%a1%86%e6%9e%b6--%e6%ad%a5%e9%aa%a4" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>&lt;img loading="lazy" src='https://cdn.acwing.com/media/article/image/2021/06/27/94631_e0a41162d7-IMG_20210627_213210_edit_157871044713930.jpg' alt="IMG_20210627_213210_edit_157871044713930.jpg">&lt;/p>
&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>堆优化版 Dijkstra 适用于稀疏图，故用邻接表存储图。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h3 id="优化思路" class="heading-element">&lt;span>优化思路&lt;/span>
 &lt;a href="#%e4%bc%98%e5%8c%96%e6%80%9d%e8%b7%af" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>1、朴素 Dijkstra 中，最慢的一步是每轮迭代中，寻找所有未更新过其他点的点中，距离起点最近的点，我们可以用小根堆来维护这个所有未更新过其他点的点构成的集合，这样每次查找最小值，就是 $O(1)$ 的时间复杂度，一共迭代 $n$ 次，所以是 $O(n)$。&lt;/p>
&lt;p>2、这样用这个最小点去更新其他所有点的距离的时间复杂度，就由 $O(m)$ 变为了 $O(m\log n)$，因为在堆中修改一个数的时间复杂度是 $O(\log n)$ 的。故总时间复杂度是 $O(m\log n)$。&lt;/p>
&lt;p>3、另外由于 STL 内的优先队列写法，堆中不支持修改任意元素，修改体现在往堆中添加一个新元素，这样会造成堆中元素冗余，堆中元素可能是 $m$ 个，这样时间复杂度就会退化至 $O(m\log m)$，但由于一般 $m \leq n^{2}$，故 $\log m \leq \log(n^{2}) = \log m \leq 2\log n$，时间复杂度接近，所以一般不用手写堆，直接用 STL 内的优先队列即可。&lt;/p>
&lt;p>另外，Dijkstra 可以算是 BFS 的升级版，就是说如果求最短路径，当图从无权值变成有权值时，BFS 不再适用了，于是我们用 Dijkstra 方法。换句话说，对于无权值图，Dijkstra 方法跟 BFS 是一致的。你可以画个无权图，用 Dijkstra 走一遍，发现其实这就是 BFS。&lt;/p>
&lt;div class="details admonition tip open disabled">
 &lt;div class="details-summary admonition-title">&lt;i class="icon fa-fw fa-regular fa-lightbulb" aria-hidden="true">&lt;/i>此处邻接表中不需要特殊考虑重边，因为算法保证了一定能够选择最短的边。&lt;/div>
 &lt;div class="details-content">
 &lt;div class="admonition-content">&lt;/div>
 &lt;/div>
&lt;/div>&lt;h3 id="完整代码" class="heading-element">&lt;span>完整代码&lt;/span>
 &lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;h4 id="c" class="heading-element">&lt;span>C++&lt;/span>
 &lt;a href="#c" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstring&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;algorithm&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;queue&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">typedef&lt;/span> &lt;span class="n">pair&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">PII&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">200010&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 可能存在重边，需要开大一点
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">idx&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// w[i]表示当前这个结点所连的下一条边权
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 当前点到初始点的最短距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 标记当前点的最短距离是否已经确定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">idx&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">dijkstra&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x3f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">priority_queue&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">PII&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">PII&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">greater&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">PII&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">heap&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 小根堆的写法，不用过多深究，记住即可
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">heap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">heap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">heap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">top&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">heap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">pop&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">ver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">second&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="k">continue&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// st[ver]为真表示当前这个点是堆中备份点，已经被处理过
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 遍历t的所有出边
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">])&lt;/span> &lt;span class="c1">// 依旧是用t更新其他所有直连点距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">heap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">push&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mh">0x3f3f3f3f&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">h&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 堆优化版Dijkstra不用特殊处理重边和自环，因为算法本身会选择最短边
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dijkstra&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="python3" class="heading-element">&lt;span>Python3&lt;/span>
 &lt;a href="#python3" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python3" data-lang="python3">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">idx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">idx&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">idx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">idx&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">dijkstra&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">queue&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PriorityQueue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PriorityQueue&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">put&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;inf&amp;#39;&lt;/span>&lt;span class="p">)]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">qsize&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="c1"># .get() 相当于 .front(), .pop()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ver&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">]:&lt;/span> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">h&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">ver&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">distance&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">q&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">put&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">float&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;inf&amp;#39;&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">M&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">2e5&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">2e5&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">input&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">h&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">w&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">M&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">M&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">M&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">idx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">st&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">input&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">%d&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">dijkstra&lt;/span>&lt;span class="p">())&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>Dijkstra求最短路I</title><link>https://yitaonote.com/2025/c2f4d7e/</link><pubDate>Sat, 16 Aug 2025 17:33:23 +0800</pubDate><guid>https://yitaonote.com/2025/c2f4d7e/</guid><category domain="https://yitaonote.com/categories/%E7%AE%97%E6%B3%95/">算法</category><description>&lt;h2 id="朴素-dijkstra-算法" class="heading-element">&lt;span>朴素 Dijkstra 算法&lt;/span>
 &lt;a href="#%e6%9c%b4%e7%b4%a0-dijkstra-%e7%ae%97%e6%b3%95" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h2>&lt;h3 id="算法框架" class="heading-element">&lt;span>算法框架&lt;/span>
 &lt;a href="#%e7%ae%97%e6%b3%95%e6%a1%86%e6%9e%b6" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>&lt;img loading="lazy" src='https://cdn.acwing.com/media/article/image/2021/06/27/94631_35d5affad7-IMG_20210627_211108_edit_157472230052533.jpg' alt="IMG_20210627_211108_edit_157472230052533.jpg">&lt;/p>
&lt;h3 id="算法步骤" class="heading-element">&lt;span>算法步骤&lt;/span>
 &lt;a href="#%e7%ae%97%e6%b3%95%e6%ad%a5%e9%aa%a4" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>适用于稠密图，故用邻接矩阵存储图&lt;/p>
&lt;p>&lt;code>st[]&lt;/code>存储所有当前已经更新过其他点的点。&lt;/p>
&lt;p>1、遍历 $n$ 次，找到当前不在&lt;code>st&lt;/code>集合中的，距离起点最近的点，赋给&lt;code>t&lt;/code>&lt;/p>
&lt;p>2、用&lt;code>t&lt;/code>去更新其他所有能够直连的点的距离：&lt;code>dist[x] &amp;gt; dist[t] + w&lt;/code>若真，就更新&lt;code>dist[x]&lt;/code>。&lt;/p>
&lt;h3 id="证明" class="heading-element">&lt;span>证明&lt;/span>
 &lt;a href="#%e8%af%81%e6%98%8e" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;p>基于贪心，无需掌握，过程略。&lt;/p>
&lt;div class="alert alert-tip">&lt;p class="alert-title">&lt;svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">&lt;path d="M8 1.5c-2.363.0-4 1.69-4 3.75.0.984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75.0 01-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456.0 00-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863.0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751.0 01-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304.0-2.06-1.637-3.75-4-3.75zM5.75 12h4.5a.75.75.0 010 1.5h-4.5a.75.75.0 010-1.5zM6 15.25a.75.75.0 01.75-.75h2.5a.75.75.0 010 1.5h-2.5A.75.75.0 016 15.25z"/>&lt;/svg>提示&lt;/p>&lt;p>每一轮迭代，在还没有更新过其他点的所有点中，找到距离起点最近的点&lt;code>t&lt;/code>，然后用&lt;code>t&lt;/code>去更新其他所有点到起点的距离。&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 为了方便找这样一个距离起点最近的点，先让 t = -1，以便加入起点循环查找
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/div>&lt;h3 id="完整代码" class="heading-element">&lt;span>完整代码&lt;/span>
 &lt;a href="#%e5%ae%8c%e6%95%b4%e4%bb%a3%e7%a0%81" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstring&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;algorithm&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">510&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// 当前点到初始点的最短距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">bool&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// st数组更确切的含义是某个点是否已经更新过其他点，而不是它的最短距离是否已经确定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">dijkstra&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x3f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">j&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 实际上，这里也只用t去更新了其他与t相邻的点的距离，只更新了相邻节点，因为不是相邻的话
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// g[t][j] 为 INF，相当于没有更新。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="o">++&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]);&lt;/span> &lt;span class="c1">// 用t去更新其他点的距离
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mh">0x3f3f3f3f&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cin&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mh">0x3f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">m&lt;/span> &lt;span class="o">--&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">scanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;%d%d%d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// 处理自环和重边
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dijkstra&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h4 id="python3" class="heading-element">&lt;span>Python3&lt;/span>
 &lt;a href="#python3" class="heading-mark">
 &lt;svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">&lt;path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z">&lt;/path>&lt;/svg>
 &lt;/a>
&lt;/h4>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python3" data-lang="python3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">N&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">M&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">510&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1e5&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">st&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="kc">False&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">dijkstra&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">t&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">t&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">j&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">st&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">j&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">j&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">):&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dist&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">n&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">input&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mf">1e9&lt;/span>&lt;span class="p">)]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">N&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">_&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="nb">range&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">N&lt;/span>&lt;span class="p">)]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span> &lt;span class="o">-=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">input&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">min&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">g&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dijkstra&lt;/span>&lt;span class="p">())&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item></channel></rss>