<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Roofline on Jiang Yi(姜祎)'s Homepage</title><link>https://jiangyigithub.github.io/ai.github.io/tags/roofline/</link><description>Recent content in Roofline on Jiang Yi(姜祎)'s Homepage</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Wed, 08 Apr 2026 21:44:44 +0800</lastBuildDate><atom:link href="https://jiangyigithub.github.io/ai.github.io/tags/roofline/index.xml" rel="self" type="application/rss+xml"/><item><title>Notes on roofline model</title><link>https://jiangyigithub.github.io/ai.github.io/p/notes-on-roofline-model/</link><pubDate>Thu, 26 Feb 2026 17:23:40 +0800</pubDate><guid>https://jiangyigithub.github.io/ai.github.io/p/notes-on-roofline-model/</guid><description>&lt;h2 id="introduction"&gt;&lt;a href="#introduction" class="header-anchor"&gt;&lt;/a&gt;Introduction
&lt;/h2&gt;&lt;p&gt;当我们在硬件上运行算法时，我们的算法效率取决于三个因素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计算机的计算效率，也就是每秒可执行的操作数 (OPs/s)&lt;/li&gt;
&lt;li&gt;数据移动效率，也就是每秒可传输的 bytes (bytes/s)&lt;/li&gt;
&lt;li&gt;需要的内存空间 (bytes)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过分析这三个因素，我们可以给出一个计算的上下界&lt;/p&gt;
&lt;h2 id="arithmetic-intensity"&gt;&lt;a href="#arithmetic-intensity" class="header-anchor"&gt;&lt;/a&gt;Arithmetic Intensity
&lt;/h2&gt;&lt;p&gt;首先，我们要回答的第一个问题是，运行时间意味着什么？为什么实际运行时间比理想时间要更多？&lt;/p&gt;
&lt;p&gt;容易知道，实际的运行时间由三部分组成&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Computation&lt;/strong&gt; 首先，运行时间与我们的算法和 GPU 设备相关，在深度学习中，我们的算法通常由 FLOPs 决定，GPU 决定了算法的运行时间：&lt;/p&gt;
$$
T_{\mathrm{math}} =\frac{\text{Computation FLOPs}}{\text{Accelerator FLOPs/s}}
$$&lt;p&gt;&lt;strong&gt;Communication within a chip&lt;/strong&gt; 其次，运行时间还受加载数据的速度的影响，在执行计算之前，我们需要先把数据从 HBM 加载到对应的 core 上，因此，加载数据的速度也会影响最终运行时间。我们将加载数据的速度称之为 &lt;strong&gt;HBM bandwidth&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Communication between chips&lt;/strong&gt; 最后，当我们在多个 GPU 上运行模型时，我们还需要在 GPU 之间进行通信，相关的通信手段如下表所示 (intra node 代表同一台服务器，inter node 代表不同服务器)，我们将 GPU 的通信速度称之为 &lt;strong&gt;Network bandwidth&lt;/strong&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Data Path&lt;/th&gt;
&lt;th&gt;BandWith&lt;/th&gt;
&lt;th&gt;Use cases&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Intra Node&lt;/td&gt;
&lt;td&gt;NVLink&lt;/td&gt;
&lt;td&gt;Direct GPU-to-GPU&lt;/td&gt;
&lt;td&gt;1.8 Tb/s (B200)&lt;/td&gt;
&lt;td&gt;TP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;NVSwith&lt;/td&gt;
&lt;td&gt;all-to-all&lt;/td&gt;
&lt;td&gt;900 GB/s-1.8T b/s&lt;/td&gt;
&lt;td&gt;full mesh sync in DGX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;GPUDirect P2P&lt;/td&gt;
&lt;td&gt;Direct over PCIe Bus&lt;/td&gt;
&lt;td&gt;32-128 GB/s&lt;/td&gt;
&lt;td&gt;small clusters withou NVLink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Shared Memory&lt;/td&gt;
&lt;td&gt;GPU -&amp;gt; CPU RAM -&amp;gt; GPU&lt;/td&gt;
&lt;td&gt;20-50 GB/s&lt;/td&gt;
&lt;td&gt;Fallback when P2P is disabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter Node&lt;/td&gt;
&lt;td&gt;InfiniBand&lt;/td&gt;
&lt;td&gt;GPU -&amp;gt; NIC -&amp;gt; IB Switch&lt;/td&gt;
&lt;td&gt;200-400 Gbps&lt;/td&gt;
&lt;td&gt;Large scale LLM training&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;GPU Direct RDMA&lt;/td&gt;
&lt;td&gt;GPU -&amp;gt; NIC -&amp;gt; Network&lt;/td&gt;
&lt;td&gt;Same as NIC speed&lt;/td&gt;
&lt;td&gt;Bypassing CPU for network sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;RoCE (v2)&lt;/td&gt;
&lt;td&gt;RAME over Ethernet&lt;/td&gt;
&lt;td&gt;100-400 Gbps&lt;/td&gt;
&lt;td&gt;Cost-effective Ethernet clusters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;TCP/IP sockets&lt;/td&gt;
&lt;td&gt;GPU -&amp;gt; CPU -&amp;gt; Kernel -&amp;gt; Net&lt;/td&gt;
&lt;td&gt;&amp;lt; 100 Gbps&lt;/td&gt;
&lt;td&gt;Standard networking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们将 GPU 的通信 (intra-node 和 inter-node) 效率定义如下&lt;/p&gt;
$$
T_{\mathrm{comm}} =\frac{\text{Communication Bytes}}{\text{Network/Memory Bandwith Bytes/s}}
$$&lt;p&gt;通常来说，我们可以通过计算 - 通信重叠来降低整体的运行时间，比如说 &lt;a class="link" href="DeepSeek-V3.md" &gt;DeepSeek-V3&lt;/a&gt; 等模型提出的优化方法, 此时理论上的最小运行时间就是 $T_{\mathrm{math}}$ 和 $T_{\mathrm{comm}}$ 之间的最大值。另一方面，如果我们不做任何优化，则理论上最大运行时间就是 $T_{\mathrm{math}}$ 和 $T_{\mathrm{comm}}$ 之和。我们定义对应的 bound 如下&lt;/p&gt;
$$
\begin{aligned}
T_{lower} &amp;= \max(T_{\mathrm{math}} ,T_{\mathrm{comm}} )\\
T_{upper} &amp;= T_{\mathrm{math}}+T_{\mathrm{comm}}
\end{aligned}
$$&lt;p&gt;注意到&lt;/p&gt;
$$
T_{upper} = T_{\mathrm{math}}+T_{\mathrm{comm}} \leq 2\max(T_{\mathrm{math}} ,T_{\mathrm{comm}} )= 2T_{lower}
$$&lt;p&gt;也就是&lt;strong&gt;最大运行时间最多为最小运行时间的两倍&lt;/strong&gt;。因此，我们一般直接优化 $T_{lower}$.&lt;/p&gt;
&lt;p&gt;如果我们假设我们可以完全达到计算 - 通信重叠，则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当 $T_{\mathrm{math}}&gt;T_{\mathrm{comm}}$ 时, 我们完全利用了 GPU 的资源，因为此时运行时间受硬件的计算效率约束，我们将其称之为 &lt;strong&gt;compute bound&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;当 $T_{\mathrm{comm}}&gt; T_{\mathrm{math}}$ 时, 此时运行时间受硬件的通信效率影响，GPU 的（计算）利用率不完全，我们将其称为 &lt;strong&gt;communication bound&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了评估一个算法时 compute bound 还是 communication bound, 我们可以使用 arithmetic intensity 来进行评估。我们先分析 single GPU 的情况，再分析 multi GPU 的情况&lt;/p&gt;
&lt;h2 id="single-gpu"&gt;&lt;a href="#single-gpu" class="header-anchor"&gt;&lt;/a&gt;Single GPU
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Arithmetic Intensity&lt;/strong&gt;
一个算法的 arithmetic intensity 定义为算法通信所需要的 FLOPs 和所需要的 bytes 之比：&lt;/p&gt;
$$
\text{Arithmetic Intensity} = \frac{\text{Computation FLOPs}}{\text{Communication Bytes}}
$$&lt;p&gt;arithmetic intensity 衡量了一个算法的 &lt;em&gt;FLOPs per bytes&lt;/em&gt;, 当 arithmetic intensity 比较高时，说明 $T_{\mathrm{math}}&gt;T_{\mathrm{comm}}$, 此时 GPU 的利用效率较高，反之则说明 GPU 出现了空置的情况。这两种情况的交叉点由 GPU 的 &lt;strong&gt;Peak arithmetic intensity&lt;/strong&gt; 决定：&lt;/p&gt;
$$
\begin{aligned}
T_{\mathrm{math}}=T_{\mathrm{comm}} &amp;\Leftrightarrow \frac{\text{Computation FLOPs}}{\text{Accelerator FLOPs/s}} = \frac{\text{Communication Bytes}}{\text{Bandwith Bytes/s}}\\
&amp;\Leftrightarrow \frac{\text{Computation FLOPs}}{\text{Communication Bytes}} = \frac{\text{Accelerator FLOPs/s}}{\text{Bandwith Bytes/s}}\\
&amp;\Leftrightarrow \text{Intensity}(\text{Computation}) = \text{Intensity}(\text{GPU})
\end{aligned}
$$&lt;p&gt;这里 $\text{Intensity}(\text{GPU})$ 就是 GPU 达到 peak FLOPs 时对应的 arithmetic intensity.&lt;/p&gt;
&lt;p&gt;不同 GPU Tensor Core 对应的 intensity 如下所示&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;bandwidth&lt;/th&gt;
&lt;th&gt;peak BF16 Tensor Core&lt;/th&gt;
&lt;th&gt;intensity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;V100&lt;/td&gt;
&lt;td&gt;Volta&lt;/td&gt;
&lt;td&gt;300 GB/s&lt;/td&gt;
&lt;td&gt;125 TFLOPs/s&lt;/td&gt;
&lt;td&gt;427&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A100&lt;/td&gt;
&lt;td&gt;Amper&lt;/td&gt;
&lt;td&gt;1,935 GB/s&lt;/td&gt;
&lt;td&gt;156 TFLOPs/s&lt;/td&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H100&lt;/td&gt;
&lt;td&gt;Hopper&lt;/td&gt;
&lt;td&gt;3.35TB/s&lt;/td&gt;
&lt;td&gt;495 TFLOPs/s&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H200&lt;/td&gt;
&lt;td&gt;Hopper&lt;/td&gt;
&lt;td&gt;4.8TB/s&lt;/td&gt;
&lt;td&gt;495 TFLOPs/s&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B200&lt;/td&gt;
&lt;td&gt;Blackwell&lt;/td&gt;
&lt;td&gt;576 TB/s&lt;/td&gt;
&lt;td&gt;2250 TFLOPs/s&lt;/td&gt;
&lt;td&gt;3.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;
假设我们使用 BF16 精度计算两个长度为 $N$ 的向量的内积，计算时的流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从 HBM 中加载两个向量，通信量为 $2*2N$&lt;/li&gt;
&lt;li&gt;计算两个向量内积，需要 $N$ 次乘法和 $N-1$ 次加法&lt;/li&gt;
&lt;li&gt;将结果写回 HBM, 通信量为 $2$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;从而内积的 intensity 为&lt;/p&gt;
$$
\text{Intensity}(\text{dot product}) = \frac{\text{Total FLOPs}}{\text{Total Bytes}} = \frac{N+N-1}{2N+2N+2} = \frac{2N-1}{4N+2}\to \frac12
$$&lt;p&gt;也就是说，内积的 arithmetic intensity 最大为 $1/2$, 说明这是一个 memory-bound operation.&lt;/p&gt;
&lt;p&gt;接下来，我们可以对 communication-bound 和 compute-bound 进行可视化，如下图所示&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jiangyigithub.github.io/ai.github.io/p/notes-on-roofline-model/GPU-roofline-model.png"
width="1400"
height="1167"
loading="lazy"
alt="Roofline model visualization for two algorithms (sourced from ‘How to Scale Your Model’)"
class="gallery-image"
data-flex-grow="119"
data-flex-basis="287px"
&gt;&lt;/p&gt;
&lt;p&gt;这里我们展示了两个算法 Algo 1 和 Algo 2 的 arithmetic intensity, 当 intensity 较小（小于硬件的 intensity）是，算法为 communication bound, 随着 intensity 增加，算法逐渐由 communication bound 过渡到 compute bound. 上面这个模型被称为 &lt;em&gt;Roofline model&lt;/em&gt;, 其展示了算法计算效率与硬件之间的关系。&lt;/p&gt;
&lt;p&gt;为了提高算法计算效率，我们可以通过提升算法的 arithmetic intensity 或者提高 memory bandwith.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;接下来我们来看一下矩阵计算的 intensity, 我们仍然假设在 BF16 精度下进行计算，假设输入矩阵为 $X\in\mathbb{R}^{b\times d}$ 以及 $Y\in\mathbb{R}^{d\times f}$ , 则输出为 $Z=XY\in\mathbb{R}^{b\times f}$. 计算过程为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从 HBM 中加载 $X$ 和 $Y$, 通信量为 $2df+2bd$ bytes&lt;/li&gt;
&lt;li&gt;计算矩阵乘法，计算量为 $2bdf$ FLOPs&lt;/li&gt;
&lt;li&gt;将结果写回到 HBM, 通信量为 $2bf$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此对应的 intensity 为&lt;/p&gt;
$$
\text{Intensity}(\text{MatMul}) = \frac{2bdf}{2bd+2df+2bf} = \frac{bfd}{bd+df+bf}
$$&lt;p&gt;假设 $b &lt;&lt; \min(d,f)$ (在 LLM 中这个假设一般成立)，我们有&lt;/p&gt;
$$
\text{Intensity}(\text{MatMul}) = \frac{bfd}{bd+df+bf}\approx \frac{bdf}{df}=b
$$&lt;p&gt;因此我们就可以通过调整 $b$ (batch size) 来将算法从 memory-bound 转换为 compute bound.&lt;/p&gt;
&lt;h2 id="multi-gpu"&gt;&lt;a href="#multi-gpu" class="header-anchor"&gt;&lt;/a&gt;Multi GPU
&lt;/h2&gt;&lt;p&gt;在 multi GPU 的场景下，我们需要关注 GPU 之间通信的效率，一般来说，GPU 之间效率要远小于 GPU 内部通信效率，下面是不同 GPU 的内部通信（HBM bandwidth） 和 GPU 之间通信效率对比&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU&lt;/th&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;HBM Type&lt;/th&gt;
&lt;th&gt;HBM bandwidth&lt;/th&gt;
&lt;th&gt;NVLink Generation&lt;/th&gt;
&lt;th&gt;NVLink Bandwidth&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;V100&lt;/td&gt;
&lt;td&gt;Volta&lt;/td&gt;
&lt;td&gt;HBM2&lt;/td&gt;
&lt;td&gt;300 GB/s&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;300 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A100&lt;/td&gt;
&lt;td&gt;Amper&lt;/td&gt;
&lt;td&gt;HBM2e&lt;/td&gt;
&lt;td&gt;1,935 GB/s&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;td&gt;600 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H100&lt;/td&gt;
&lt;td&gt;Hopper&lt;/td&gt;
&lt;td&gt;HBM3&lt;/td&gt;
&lt;td&gt;3.35TB/s&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;900 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H200&lt;/td&gt;
&lt;td&gt;Hopper&lt;/td&gt;
&lt;td&gt;HBM3e&lt;/td&gt;
&lt;td&gt;4.8TB/s&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;900 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B200&lt;/td&gt;
&lt;td&gt;Blackwell&lt;/td&gt;
&lt;td&gt;HBM3e&lt;/td&gt;
&lt;td&gt;576 TB/s&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;td&gt;1800 GB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;
我们考虑一个分布式矩阵乘法，假设我们有两个 GPU, 计算精度为 BF16, 矩阵 $X\in\mathbb{R}^{b\times d}$ 以及 $Y\in\mathbb{R}^{d\times f}$ 分别被切分为相同大小存储在这两个 GPU 上，此时，计算流程为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 GPU0/GPU1 上，分别从对应的 HBM 中加载 $X_1, Y_1$ 以及 $X_2, Y_2$&lt;/li&gt;
&lt;li&gt;在 GPU0/GPU1 上，分别计算对应的矩阵乘法 $A=X_1Y_1\in\mathbb{R}^{b\times f}$ 和 $B=X_2Y_2\in\mathbb{R}^{b\times f}$, 计算量为 $2bdf$.&lt;/li&gt;
&lt;li&gt;将 GPU1 的结果 $B$ 通信传输到 GPU0 上，通信量为 $2bf$.&lt;/li&gt;
&lt;li&gt;对结果进行相加，$Z=A+B$. 计算量为 $bf$.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;此时，我们的 intensity 为 （注意这里计算时间除以 2 代表我们使用两张 GPU 因而效率翻倍了）&lt;/p&gt;
$$
\text{Intensity}(\text{MatMul (2-GPU)}) = \frac{2bfd/2}{2bf}= \frac{d}{2}
$$&lt;p&gt;可以看到，此时决定性因素在于 $d$ 而不是 $b$, 这对于我们实现并行算法至关重要&lt;/p&gt;</description></item></channel></rss>