想象你是一家餐厅的老板。后厨里有两位“员工”:一位是掌勺大厨,手艺再快,一小时也只能颠那么多次锅;一位是传菜小哥,腿脚再利索,一趟也搬不了太多食材。你想知道餐厅一小时最多能出多少道菜——只看大厨或只看小哥都不行,必须把两者放在同一张图上看。Roofline 模型就是计算机界的“后厨产能地图”:纵轴画出硬件的计算峰值,横轴标出程序的运算强度,两条约束线画成一个“屋顶”,程序就是屋顶下的一个点。点落在屋顶的哪一侧,瓶颈就在哪里。这张图让程序员从“玄学调优”变成“地图导航”——先看清自己站在哪,再决定往哪个方向跑。无论是跑深度学习训练、科学计算还是大数据处理,它都能一针见血地告诉你:你的程序到底是“没力气搬数据”还是“没功夫做计算”。
掌握 Roofline 模型,你就拥有了性能优化领域的“第一性原理”:一眼定位瓶颈、理性选择优化方向,避免在错误路径上浪费数周调优时间。
可视化演示
Roofline模型通过运算强度将计算性能与内存带宽统一分析,直观展示性能瓶颈。本文讲解Roofline模型的... 切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Roofli:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现
基本原理与坐标轴
经营餐厅时你很快会发现:只盯着大厨的颠勺速度没用,传菜通道的搬运速度同样决定出餐上限。但两者的单位完全不同,怎么放在一起比?聪明的做法是——不直接比“速度”,而是画一张图:纵轴是“出餐产能”,横轴是“每道菜需要的功夫与搬运的比值”。Roofline 模型正是用这个思路,把计算机的两种根本约束统一到一张二维地图上。
2009 年,Samuel Williams 等人提出了 Roofline 模型。它不关心程序内部的分支、循环等细节,而是聚焦于两个核心约束:硬件能算多快(计算峰值),以及数据能搬多快(内存带宽)。模型的纵轴是“可达到的浮点性能”,单位 FLOP/s;横轴是“运算强度”,单位 FLOP/Byte。由于真实场景中性能与运算强度常常跨越好几个数量级,这张图使用了对数坐标——就像地图用对数刻度,才能同时容纳蚂蚁窝和高楼大厦。
有了这张“后厨地图”的骨架,接下来我们要定义横轴——每道菜的“功夫与搬运比”,也就是运算强度。只有把这个坐标定义清楚,地图才能投入使用。
- 分子 W(浮点运算量)算法总共要完成的浮点运算次数(加、减、乘、除等),是“要做多少活”。
- 分母 T(运行时间)程序从开始到结束消耗的墙钟时间,单位秒,是“花了多久”。
- 比值 P(平均性能)单位时间内完成的浮点运算数,也是 Roofline 图上每个点的纵坐标。
运算强度定义
做一道菜,既要花烹饪功夫,也要消耗食材。有的菜(如佛跳墙)工序极多但食材用量精小;有的菜(如大锅炖土豆)几乎不用刀工,但土豆一搬就是半吨。Roofline 模型把“这道菜值不值得做”的核心指标称为运算强度(Arithmetic Intensity):它刻画的是“每搬运一单位食材,能完成多少单位烹饪工作”。
在计算机里,食材就是数据,烹饪工具就是浮点运算单元。运算强度的定义很简洁:浮点运算量除以访存量。它是一个程序自身的属性,与硬件无关——同一段代码,放在任何一台机器上,它的运算强度都不变。程序想知道自己在 Roofline 图中的横坐标是多少,就看这个比值。典型场景中,逐元素向量操作(如 SAXPY)强度常常不足 1 FLOP/Byte,属于“大锅菜”;而稠密矩阵乘法可以达到几十甚至上百 FLOP/Byte,属于“功夫菜”。
运算强度就像菜品的“功夫食材比”:比值高,说明瓶颈更可能出在厨艺(计算);比值低,说明瓶颈更可能在搬运(访存)。有了这个横坐标,我们就可以把屋顶盖起来了。
- 分子 W(浮点运算量)算法所需要的全部浮点运算次数,统计的是“动作总量”。
- 分母 Q(访存量)程序运行过程中从主存读入和写出的总字节数,统计的是“搬运总量”。
- 比值 I(运算强度)两者相除,得到“每搬运 1 字节能做的运算数”,单位是 FLOP/Byte。
屋顶线构成
后厨的产能天花板有两层:一层是灶台本身的极限——锅再热、手再快,一秒也就翻那么几下;另一层是传菜通道的极限——小哥腿再快,通道就只有那么宽。真正的出餐上限,永远由较低的那一层决定。Roofline 模型把这两层天花板画成两条线的“屋顶”:一条水平线,一条斜线。
水平线来自硬件的计算峰值(\(\pi\)),即理想条件下每秒最多能完成的浮点运算次数。斜线来自内存带宽(\(\beta\)):带宽屋顶 = 运算强度 × 内存带宽。运算强度越高,说明每个数据被复用得越充分,带宽能支撑起的性能也就越高,所以它是一条向上倾斜的线。两条线的交叉点称为脊点(Ridge Point),它是整张地图的“分水岭”。
屋顶盖好后,地图就完整了。脊点像后厨里“灶台恰好匹配传菜速度”的黄金位——往左是传菜通道拖后腿,往右是大厨忙不过来。下一步,我们要学会看地图:程序点落在哪一侧,怎么判断瓶颈。
- 分子 \(\pi\)计算峰值,代表灶台全开的速度。
- 分母 \(\beta\)内存带宽,代表传菜通道的运力。
- 比值 \(I_0\)脊点的横坐标,两条屋顶线在此交汇,划分出两个完全不同的受限区域。
瓶颈判断方法
把一道新菜谱放到产能地图上。如果它的“功夫食材比”落在脊点左侧,说明搬运食材的速度跟不上做菜速度——哪怕灶台全开,也只能干等食材入场。反之,如果落在右侧,说明做菜速度跟不上搬运——食材堆满了台面,锅铲却翻不过来。Roofline 模型的实用价值正在于此:它用一个点的横坐标,直接告诉你瓶颈在哪里。
程序点在 Roofline 图中的位置由运算强度 \(I\) 决定。将 \(I\) 与脊点 \(I_0\) 比较:当 \(I < I_0\) 时,程序处于内存受限(Memory-bound)区域,它的性能天花板是斜线;当 \(I \ge I_0\) 时,程序处于计算受限(Compute-bound)区域,天花板是水平线。值得注意的是,边界不是静止的——当你通过优化减少了访存量,点的运算强度会增大,它就会向右移动,可能从内存受限区跨入计算受限区。
这就像餐厅瓶颈诊断:传菜通道堵了就加传菜员,灶台不够用了就换大锅或简化工序。诊断错了方向,再努力也是白费。接下来我们把地图切换到不同的“后厨”——GPU 巨型中央厨房和 CPU 私房小馆,看看同样的屋顶能长出多不一样的形态。
- 待比较的量 \(I\) 与 \(\pi/\beta\)程序横坐标与脊点横坐标作比较,看谁大谁小。
- 左侧情形 \(I < \pi/\beta\)带宽屋顶低于计算屋顶,上限由内存带宽压住,此时应削减访存量 \(Q\)。
- 右侧情形 \(I \ge \pi/\beta\)计算屋顶低于带宽屋顶,上限由算力压住,此时应削减运算量 \(W\) 或提高算力利用率。
硬件架构差异
一家现代化中央厨房(GPU)灶台多到数不清,但仓库通道就那么几条;一家精品私房菜馆(CPU)灶台不多,可调料柜就搁在手边。同样是做菜,两家的瓶颈形态天差地别。Roofline 模型的价值之一,就是能把这种硬件差异直观地翻译成屋顶形状的差异。
GPU 的峰值算力极高,内存带宽虽然也不低(如 HBM 高带宽显存),但算力的增幅往往远大于带宽的增幅,因此它的脊点通常更靠右。这带来一个反直觉的结论:很多在 CPU 上表现正常的程序,搬到 GPU 上反而成了内存受限——运算强度不够,连斜屋顶都没爬到。CPU 则刚好相反,其算力与带宽的比值更平衡,脊点偏左,单核程序更容易落在计算受限区。架构参数如核心数、内存通道数、缓存大小、SIMD 宽度等,都会改变屋顶的位置和坡度。
中央厨房适合“功夫菜”(高运算强度),私房小馆对“大锅菜”反而更友好。理解了硬件差异,我们还得学会给厨房“加装小冷柜”——缓存。这个改动能让屋顶原地抬高。
- GPU 脊点 \(I_0^{\text{GPU}}\)高算力除以高带宽,由于算力增幅更大,脊点通常位于横轴较右处。
- CPU 脊点 \(I_0^{\text{CPU}}\)算力与带宽的比值更小,脊点通常左移。
- 对比两个脊点两个平台的脊点位置不同,同一个程序可能在一个平台内存受限、在另一个平台计算受限。
缓存效应分析
如果厨房旁边有个小冷藏柜,很多食材就不必每次都去大仓库搬运。搬运距离短了、频次低了,有效搬运速度自然大幅提升——而这不需要新增任何传菜员。在计算机里,这个小冷藏柜就是缓存(Cache),从 L1、L2 到 L3,层层嵌套,越靠近 CPU 越快但容量越小。
缓存能起作用,靠的是数据局部性:程序短时间内反复访问同一批数据(时间局部性)或相邻数据(空间局部性)。当缓存命中率提高,真正落到主存的访存量 \(Q_{\text{主存}}\) 就会明显小于逻辑访存量 \(Q_{\text{总}}\)。从 Roofline 的视角看,这等于把内存带宽“放大”了——有效带宽 \(\beta_{\text{eff}}\) 高于硬件标称值。因此,考虑缓存的 Roofline 扩展模型会有一条更陡、更高的有效屋顶线,程序点也随之移动。
小冷柜摆得越聪明,原料就越不用跑大仓库——这就是“缓存友好”优化的本质。当屋顶被抬高后,原本内存受限的程序可能不再受限,你的优化重点也要跟着转移。现在,地图的最后一课是:怎么用它当导航仪。
- \(Q_{\text{总访存}}\)程序逻辑上需要的全部数据搬运量。
- \(Q_{\text{主存}}\)缓存未命中、真实落到主存访问的量。
- 缓存增益 \(r\)被缓存“吸收”的那部分访问占比,局部性越好 \(r\) 越大。
- 有效带宽放大主存带宽被因子 \((1+r)\) 放大,屋顶线随之抬高。
优化指导应用
拿到后厨产能地图,优化决策忽然变得清晰:如果瓶颈在传菜通道,就上自动传送带(优化访存);如果瓶颈在灶台,就换大火力(升级算力)或者改菜谱减少工序(算法优化)。Roofline 模型在工程实践中的角色,就是这张全程导航地图——先定位,再选路,最后验证。
具体而言,一套完整的优化循环包含三步:第一步,计算或测量目标程序在当前平台上的运算强度,画出它在 Roofline 图中的位置;第二步,根据它落在哪个受限区制定策略——内存受限就做循环分块、访存合并、数据压缩、缓存复用,计算受限就做向量化、算法替换、减少冗余计算;第三步,优化后重新测量,看实际性能是否贴到了新屋顶。如果优化后点仍然远离屋顶,说明优化方向没找对;如果已贴近屋顶但想更进一步,那就需要考虑换硬件或改算法结构。
一张地图,一套打法。Roofline 让性能优化从“盲人摸象”变成“按图索骥”——你永远知道自己在哪、该往哪走、走了多远。这不仅是理论工具,更是每位 AI 工程师手上的实战罗盘。
- \(I'\)(优化后运算强度)通过分块、复用等手段提高数据复用率,让点向右移动。
- \(\beta_{\text{eff}}\)(有效带宽)通过缓存友好的访存布局,把有效带宽放大。
- 重新套最小化公式优化是否有效,取决于新点离哪条屋顶更近,以及瓶颈是否已经转移。
AI 应用场景
深度学习算子选型优化
阶段:模型训练加速 / 推理优化
解决的问题:深度学习框架中矩阵乘法、卷积、归一化、残差连接等算子在 GPU 上性能差异巨大。传统做法是对每个算子逐一盲测 cuDNN、cutlass、手写 kernel,耗时且难以解释。Roofline 模型帮助工程师先计算每个算子的运算强度,对照 GPU 脊点快速判断它是访存密集还是计算密集,从而直接缩小候选方案范围。
具体案例:某 NLP 模型在 A100 上训练时,LayerNorm 与残差加算子的运算强度不足 1 FLOP/Byte,而 A100 脊点远高于此,一查即确定为内存受限。工程师将这些元素级算子融合进前一个矩阵乘法的 kernel,避免中间张量写回显存,训练吞吐提升约 22%。
HPC 科学计算性能预测
阶段:高性能计算应用调优
解决的问题:气候模拟、分子动力学等科学计算程序在超算上优化时,传统做法是凭经验进行循环展开、向量化,投入大量时间却可能收效甚微。Roofline 模型可以在动手前先判断每个热点模块属于内存受限还是计算受限,避免在错误方向白费数周。
具体案例:某气象模型团队在 CPU 集群上花两周做循环展开与向量化,性能仅提升 5%。用 Roofline 分析后发现热点模块运算强度只有 0.2 FLOP/Byte,典型内存受限。团队转而采用数据分块与缓存复用,性能提升 40%。
AI 硬件选型与采购决策
阶段:软硬件协同设计 / 平台选型
解决的问题:算法团队选训练或推理服务器时,传统方法靠跑 benchmark 反复试。Roofline 提供了理性依据:把目标模型核心算子的运算强度与候选芯片的 Roofline 参数对比,直接判断哪些芯片能让计算成为主导、哪些芯片的带宽会被浪费。
具体案例:某自动驾驶公司评估两款推理芯片:芯片 A 算力高但带宽低,芯片 B 算力适中但带宽充足。将常见检测模型的算子标注在 Roofline 图后,发现模型以低运算强度的算子为主,芯片 B 端到端延迟反而更低,且采购成本节省约 30%。
Python 演示
"""Roofline model analyzer: classify and visualize performance bottlenecks."""import numpy as npimport matplotlib.pyplot as plt def ridge_point(peak_flops, peak_bandwidth): """Compute the ridge point: peak compute / peak bandwidth.""" return peak_flops / peak_bandwidth def classify(intensity, ridge): """Return 'memory-bound' or 'compute-bound'.""" if intensity < ridge: return "memory-bound" return "compute-bound" def plot_roofline(peak_flops, peak_bandwidth, programs): """Draw the roofline curve and mark program positions.""" intensity = np.logspace(-2, 4, 300) compute_roof = np.full_like(intensity, peak_flops) memory_roof = intensity * peak_bandwidth achievable = np.minimum(compute_roof, memory_roof) ridge = ridge_point(peak_flops, peak_bandwidth) plt.figure(figsize=(10, 6)) plt.loglog(intensity, achievable, linewidth=3, label="Roofline") plt.loglog(intensity, compute_roof, "--", linewidth=1, label="Compute roof") plt.loglog(intensity, memory_roof, "--", linewidth=1, label="Memory roof") plt.axvline(ridge, color="gray", linestyle=":") plt.text(ridge * 2, peak_flops * 0.2, "Ridge = {:.1f}".format(ridge), rotation=90) for name, intensity_value in programs.items(): perf = min(peak_flops, intensity_value * peak_bandwidth) status = classify(intensity_value, ridge) plt.scatter([intensity_value], [perf], s=90, zorder=5) plt.annotate("{} ({})".format(name, status), (intensity_value, perf), xytext=(intensity_value * 3, perf * 0.4), fontsize=9) print("{}: I={:.2f} -> {}".format(name, intensity_value, status)) plt.xlabel("Arithmetic Intensity (FLOP/Byte)") plt.ylabel("Achievable Performance (FLOP/s)") plt.title("Roofline Model Analysis") plt.legend() plt.grid(True, which="both", alpha=0.3) plt.savefig("roofline.png", dpi=120) if __name__ == "__main__": PEAK_FLOPS = 20e12 # 20 TFLOPS PEAK_BANDWIDTH = 200e9 # 200 GB/s ridge = ridge_point(PEAK_FLOPS, PEAK_BANDWIDTH) print("Ridge point: {:.2f} FLOP/Byte".format(ridge)) programs = { "saxpy": 0.25, "stencil": 0.8, "gemm": 4.0, "fft": 12.0, } plot_roofline(PEAK_FLOPS, PEAK_BANDWIDTH, programs)
roofline.png 和每个程序的状态输出。intensity = np.logspace(-2, 4, 300)生成从 0.01 到 10000 的等比数列,覆盖运算强度的多个数量级,横轴采样。compute_roof = np.full_like(intensity, peak_flops)水平屋顶:任何运算强度下,性能都不能超过计算峰值。achievable = np.minimum(compute_roof, memory_roof)逐点取两条屋顶线的较小值,得到完整 Roofline 曲线,体现“木桶效应”。ridge = peak_flops / peak_bandwidth用公式 \(I_0 = \pi/\beta\) 计算脊点横坐标。perf = min(peak_flops, intensity_value * peak_bandwidth)对每个程序求它在屋顶线下的理论最高性能,即图中的点高度。status = classify(intensity_value, ridge)用程序运算强度与脊点的关系判断受限类型,是全文最核心的逻辑。
常见误区
误区我以为 Roofline 模型画出来的屋顶线就是程序实际能跑到的性能,只要我优化到点落在这条线上,就再也没法提升了。
正解屋顶线是硬件平台给出的理论天花板,程序点只能出现在屋顶线下方。点离屋顶越近,说明优化得越充分;点离屋顶越远,说明还有大量优化空间。就像后厨地图标的是“产能上限”,不等于每天真的能出这么多菜。正确的用法是持续把点“往上推”,逼近但不奢求超越屋顶。
为何常见:刚接触时被“Roofline(屋顶线)”这个命名误导,直觉上容易把它当成“实际性能曲线”,忽略了它本质是理论上界的属性。
误区我觉得运算强度应该跟着硬件走——换了 GPU 之后运算强度肯定会变大,因为它算得快啊。
正解运算强度是程序本身的属性,与硬件无关。它只取决于算法要算多少次浮点运算、要搬多少字节数据。硬件决定的不是横坐标,而是屋顶线的形状。就像同一道菜的“功夫食材比”不会因为换厨房而变化,变的只是它在新厨房地图上的相对位置。
为何常见:Roofline 图把程序和硬件画在同一张图上,初学者很容易把分属两边的属性混为一谈。
误区GPU 算力那么高,任何程序放上去都会更快,Roofline 分析是不是多此一举?
正解如果程序运算强度低(落在脊点左侧),GPU 的高算力完全被带宽压住,根本发挥不出来,甚至可能因为数据传输开销而比 CPU 更慢。这就像中央厨房灶台再多,传菜通道只有一条窄道,大锅菜照样出得慢。Roofline 的正确用法是:先看程序点在屋顶的哪一侧,再决定要不要上 GPU。
为何常见:厂商和媒体不断强调峰值算力,让大众忽略了“多数真实程序并非计算受限”这一事实。
误区内存带宽是硬件写死的参数,所以斜线屋顶应该永远不变,做缓存优化没有意义。
正解硬件标称带宽只是基础值,程序的访存模式、数据局部性以及缓存层级会大幅改变“有效带宽”。Roofline 的缓存扩展模型正是为了描述这种现象:缓存命中率高时,屋顶线会局部抬高。这相当于在厨房边加个小冷柜,原料不用每次都跑大仓库,搬运效率自然不同。
为何常见:教材上的基础 Roofline 模型没有展开缓存层次,很多人学了简化版就以为它是全貌。
误区Roofline 图上两个点离屋顶的视觉距离看起来差不多,那它们的优化空间应该也差不多吧?
正解Roofline 图几乎都用对数坐标,因为性能和运算强度跨越多个数量级。视觉上的“一倍距离”在实际数值上可能差了 10 倍甚至 100 倍。判断优化空间时,必须把对数刻度换算回真实数值,不能直接用直尺量图。地图如果用对数尺,就不能拿普通尺子读数。
为何常见:人眼天然习惯线性刻度,对数坐标的视觉偏差非常隐蔽,容易导致对瓶颈严重程度的误判。
面试题
Q1. Roofline 模型的横轴表示什么?
- A.程序运行时间
- B.运算强度(FLOP/Byte)← 正确答案
- C.内存带宽(Byte/s)
- D.缓存大小(Byte)
答案:B。Roofline 模型的横轴是运算强度,表示每搬运 1 字节数据能完成的浮点运算数;纵轴才是可达到的性能。就像后厨地图的横轴是“功夫与食材比”,而不是时间或带宽本身。
Q2. 脊点(Ridge Point)的计算公式是?
- A.计算峰值除以内存带宽← 正确答案
- B.内存带宽除以计算峰值
- C.计算峰值乘以内存带宽
- D.1 除以内存带宽
答案:A。脊点是水平计算屋顶与斜带宽屋顶的交点,横坐标 \(I_0 = \pi/\beta\),即计算峰值除以内存带宽。它标定了内存受限区与计算受限区的分界。
Q3. 某平台峰值算力为 10 GFLOP/s,内存带宽为 20 GB/s。程序 P 的运算强度为 2.0 FLOP/Byte,它属于哪类瓶颈?
- A.计算受限← 正确答案
- B.内存受限
- C.刚好平衡
- D.无法判断
答案:A。脊点 = 10/20 = 0.5 FLOP/Byte。程序 P 的运算强度 2.0 大于 0.5,落在脊点右侧,由算力屋顶限制性能,属于计算受限区域。
Q4. 在普通线性坐标系中,Roofline 模型的带宽屋顶线是什么形状?
- A.一条水平直线
- B.一条过原点的上升直线← 正确答案
- C.一条指数下降曲线
- D.一条锯齿状折线
答案:B。带宽屋顶 = 运算强度 × 内存带宽,在普通坐标下是过原点、斜率为带宽值的直线。它与水平的计算屋顶线相交形成脊点;在对数坐标下则表现为固定斜率的斜线。
Q5. 对于内存受限的程序,哪些优化方向是合理的?
- A.增加缓存复用以减少主存访问← 正确答案
- B.增加浮点运算次数以“压榨”算力
- C.合并访存或数据压缩减少搬运量← 正确答案
- D.降低 CPU 主频以减少发热
答案:AC。内存受限时天花板由带宽决定,核心思路是削减访存量 \(Q\):缓存复用、访存合并、数据压缩都能让点向右移动、突破斜屋顶。增加浮点运算会让程序更臃肿,降频则让所有性能都变差。
Q6. GPU 在 Roofline 图中通常表现出什么特点?
- A.脊点明显左移
- B.脊点明显右移← 正确答案
- C.不存在脊点
- D.屋顶线消失
答案:B。GPU 的算力增幅通常远大于带宽增幅,所以脊点 \(I_0=\pi/\beta\) 显著右移。这意味着普通程序更容易落入内存受限区,需要更高的运算强度才能发挥 GPU 的优势。
Q7. 在 Roofline 模型中引入缓存概念后,可能发生哪些变化?
- A.有效带宽提升← 正确答案
- B.程序的有效运算强度变大← 正确答案
- C.硬件的计算峰值降低
- D.屋顶线局部抬高← 正确答案
答案:ABD。缓存命中提高后,主存访存量减少,相当于放大有效带宽,所以屋顶局部抬高;同时用主存流量替代逻辑访存量计算时,有效运算强度也会变大。缓存不会改变硬件的计算峰值,C 错误。
Q8. 以下哪个因素不会直接影响 Roofline 模型屋顶线的形状?
- A.硬件的计算峰值
- B.内存带宽
- C.程序代码风格← 正确答案
- D.缓存层级与大小
答案:C。屋顶线完全由硬件平台决定,包括峰值算力、内存带宽和缓存层级。程序代码风格决定的是点落在屋顶下方的哪个位置(即运算强度),它不会移动屋顶本身。
Q9. 某平台计算峰值 40 GFLOP/s、带宽 10 GB/s。程序 A 运算强度为 3,程序 B 运算强度为 8,下列说法正确的是?
- A.A 计算受限,B 内存受限
- B.A 内存受限,B 计算受限← 正确答案
- C.两者都内存受限
- D.两者都计算受限
答案:B。脊点 = 40/10 = 4 FLOP/Byte。A 的运算强度 3 小于 4,处于内存受限区;B 的强度 8 大于 4,处于计算受限区。同一个平台上,程序性质不同,瓶颈归属也不同。
Q10. 关于 Roofline 模型,下列说法正确的有哪些?
- A.它能帮助预测程序在某平台上的性能上界← 正确答案
- B.运算强度由硬件架构决定
- C.脊点是计算屋顶与带宽屋顶的交点← 正确答案
- D.对数坐标便于展示跨数量级的性能差异← 正确答案
答案:ACD。Roofline 给出的是理论上界,程序点只会出现在屋顶线下方;脊点是两条屋顶线的交点,用于划分受限区域;对数坐标是它展示大跨度数据的关键设计。运算强度是算法属性而非硬件属性,B 错误。