第三章:Details 剩余数据补读
第二章从 Details 页里挑出了三个重点 section,这一章沿着 Details 页往下走,把剩下的 11 个 section 按页面顺序补齐,依次是 PM Sampling、Compute Workload Analysis、Scheduler Statistics、Warp State Statistics、Instruction Statistics、NVLink Topology、NVLink Tables、NUMA Affinity、Launch Statistics、Occupancy、GPU and Memory Workload Distribution,加上第二章那三个,Details 页的 14 个 section 到这一章全部读完。
PM Sampling
PM Sampling 是 Details 页的第二个 section。PM 是 Performance Monitor,性能监视器,GPU 上自带的一套计数电路,用来累计各条管线忙了多少周期、各级缓存搬了多少字节。前面读过的吞吐率、命中率等全程指标,把 kernel 从开始到结束的活动汇总成一个值;为了收齐指标,ncu 可能需要重放多次,每次采集其中一部分。PM Sampling 也可能分多次重放采集,但会在运行过程中按设定间隔读取计数器,把各采样窗口里的结果沿时间轴画出来,好似示波器那样呈现变化,得到这一节的时间线视图。
基本数据表
ncu 说明为:PM 指标在 workload 运行期间周期性采样得来的时间线视图,数据跨多个 pass 收集,这一节用来看 workload 的行为随运行时间是怎么变化的。workload 指的是这次被剖析的 kernel 运行本身;pass 表示一次采集重放。GPU 能同时采集的计数器数量有限,全部指标无法一次采齐时,ncu 就把指标分组,在不同次重放中分别采集。
说明底下是 4 个数据:
| 项 | 值 | 说明 |
|---|---|---|
| Maximum Sampling Interval | 1 us | 各采样组配置的采样间隔中的最大值,间隔越短,通常能看到越细的时间变化,也会产生更多数据 本报告两组都设为1微秒。以318.43微秒的kernel时长估算,对应约318个采样间隔;实际样本数量还受采集起止边界和样本合并影响 |
| Maximum Buffer Size | 24 Mbytes | 采样到的数据先写进缓冲区,这一行给出各采样组所用缓冲区大小的最大值 本报告两组各为25,165,824字节,即24 × 1,024 × 1,024字节,按二进制单位应写作24 MiB;截图沿用界面的24 Mbytes |
| # Pass Groups | 2 | 这次PM采样分成多少组指标采集,每组对应一次采样重放 本kernel的PM指标分成2组,分别通过两次重放采回来;这是PM Sampling自身的采集次数 |
| Dropped Samples | 0 sample | 因缓冲区装不下而丢弃的样本数 两组都为0,说明这次采集没有因缓冲区不足而丢样 |
这4项交代采集设置和采集状态:本次分2组采样,间隔都设为1微秒,也没有因缓冲区不足而丢样。还要留意样本合并:采样结果来不及及时传出时,硬件可能合并样本,本报告两组分别记录了432和421个合并样本。因此,读时间线时仍要结合样本时间戳、采样窗口和不同次重放的对齐情况判断时间细节。
时间线
展开section,得到时间线图。横轴是时间,每一行展示一个指标在各个采样窗口里的值。图中从上往下分成Overview、SM、DRAM、L2 Cache、L1 Cache五组,点击组名左侧的三角形可以折叠这一组。读具体一行时,先看左侧的指标名,再看右侧标出的纵轴范围,最右边是随时间变化的采样值。这里百分比行的刻度上限统一是100 %,其它行按采到的最大值显示。
按住Ctrl键滚动滚轮可以缩放时间轴,左键单击可以选中一个采样值,悬停时能看到对应的时间和数值。下面写到的中段数值取自报告的采样数据,都是近似读数;行名旁边的最大值则沿用截图。横条只有几十个像素高,贴近0的曲线要通过悬停提示或采样数据确认读数。
最后一行Workload Execution用黄色横条标出本kernel的运行区间。时间轴的0是采样数据对齐后的相对起点,不一定与kernel的开始时刻重合,所以黄色横条从0的右侧才开始。黄色条的左右边界来自单独采集的kernel起止时间戳;开头这段间隔只说明两个起点不同,不能直接当作kernel的启动开销。
本报告的曲线来自两次重放:Overview、SM和DRAM属于一组,L2 Cache和L1 Cache属于另一组。ncu会过滤并对齐这些采样数据,但不同次重放不一定完全对齐,采样边界也不一定与kernel的起止边界重合。读时间线主要看运行中段的状态和整体趋势。有些曲线会比黄色条更早出现,或者更晚才归零,判断kernel的执行范围仍以黄色条为准。
Overview分组:
| 指标 | 说明 |
|---|---|
| Average Active Warps Per Cycle,0 到 48.01 warp | 平均到每SM的活跃warp数,正在等数据或等同步的warp也算。本卡每两个SM组成一个TPC,采样先用TPC内累计的活跃warp周期数除以相应的TPC活跃周期数,得到平均每TPC每活跃周期驻留多少个warp,再除以2折算到每SM 开头迅速升到接近48,中段主要在46左右,末尾再下降。48是每SM的硬件驻留上限,中段46 ÷ 48约为96 %,说明大部分warp位置一直有人占着,驻留数量较充足。刻度上的48.01是本图采到的最大值,比硬件上限高约0.02 %,属于轻微越界读数。ncu按测量值绘图,判断驻留容量仍使用48个warp的硬件上限 |
| Total Active Warps Per Cycle,0 到 3.94k warp | 把各SM的活跃warp数汇总到整块GPU。82个SM各驻留48个warp时,一共是82 × 48 = 3,936个,图上的最大值显示为3.94k 这条曲线与上一行对应,中段约为46 × 82 ≈ 3,770个warp,开头上升、末尾下降。两行分别从单个SM的平均值和整块GPU的总量描述驻留情况 |
| Blocks Launched,0 到 98 block | 每个采样窗口里新启动的block数。本kernel共有8,192个block,每block有256 ÷ 32 = 8个warp。按每SM的48个warp上限计算,最多能驻留48 ÷ 8 = 6个block,当前资源配置能达到这个上限,整卡同时最多容纳82 × 6 = 492个 开头集中启动一批block,随后暂时没有新block启动,曲线降到0,但前面启动的block仍在执行。中段每个窗口大约新启动25到30个block,用来接替已经完成的block,此时上一行的活跃warp仍接近满驻。末尾新启动数降到0,只表示不再补入新block,已经驻留的block还要继续收尾 |
| SM Active Cycles,0 到 1.42k cycle | 每个采样窗口里,各SM活跃周期数的平均值。SM只要还有warp驻留就算活跃,即使这些warp正在等待,也会计入 中段约1,400周期。按1微秒窗口和约1.39 GHz的SM频率估算,1.39 × 10⁹ × 10⁻⁶ ≈ 1,390周期,说明中段多数SM几乎整个窗口都有warp驻留。开头上升对应SM陆续进入活跃状态,末尾下降对应SM陆续空下来 |
| Executed Ipc Active,0 到 1.54 inst/cycle | IPC是instructions per cycle,每个周期执行多少条指令,这里数的是warp级指令。一个窗口内,各SM平均执行的指令数除以各SM平均活跃周期数,得到每SM每个活跃周期平均执行的指令条数 起步时较低,中段约0.57,尾段上升,最高显示为1.54。一个SM有4个SMSP,每个SMSP每周期最多发射一条指令;中段每SM每活跃周期只执行约0.57条,说明驻留虽充足,指令执行速率仍低。最后这条曲线尾段抬高,很容易被读成"剩下的block不用竞争,所以跑得更快了"。我们把分子、分母拆开看:中段一个窗口的IPC约为0.576,平均活跃周期约1,396,折算每SM约执行0.576 × 1,396 ≈ 804条指令;尾段IPC达到约1.535的窗口,平均活跃周期却只剩46.49,折算约1.535 × 46.49 ≈ 71条。IPC升高,窗口里的指令总量反而下降了。图上能确认的是活跃周期这个分母下降得更快,剩下的活跃周期里指令执行更密;具体是哪类等待减少、哪段代码占比改变,仅凭这曲线还分不出来。这里统计的是执行侧IPC,要精确读发射槽占用,应使用发射侧指标 |
SM分组:
这一组按SM及其内部SMSP的硬件时钟数周期,SMSP就是第二章介绍过的SM子分区,与所属SM使用同一时钟。SM频率约1.39 GHz,表示SM的时钟每秒走过约13.9亿个周期,1微秒内约有1,390个周期。采样窗口里的这些周期全部计入"流逝周期",其中也包括SM没有warp驻留的周期。
Overview里的Executed Ipc Active只把SM有warp驻留的周期计入分母。举个计算例子:假设一个SM在某个窗口里流逝了1,000个周期,其中600个周期有warp驻留,那么它的流逝周期数是1,000,活跃周期数是600。warp驻留期间即使在等数据,这些周期仍算SM活跃周期。这样,两种分母分别数什么就清楚了。
接下来我们要读管线的百分比。以ALU为例,分子是窗口内ALU实际执行的指令数,分母是"该SM在窗口内的流逝周期数 × ALU每周期可持续执行的指令数",相除再乘100 %。分母表示这条管线在整个窗口里持续吃满时能执行多少条指令。FMA Light和FMA Heavy在SMSP层统计,使用对应SMSP的流逝周期数和对应管线的峰值。Tensor这一行数的是管线活跃周期。各条管线能执行的指令类型和每周期的峰值不同,所以每行的100 %各指自己的满负荷水平,SM Throughput按这些组成指标中的最高值统计。
| 指标 | 说明 |
|---|---|
| SM Throughput,0 到 100 % | 先分别计算SM各组成指标的"实际处理速率 ÷ 对应峰值 × 100 %",再取其中最高值,得到本窗口里SM最忙执行资源相对自身峰值的利用率;下面四条管线只展示组成指标的一部分 运行中段约14 %,表示这套采样指标中利用率最高的SM执行资源,也只达到自身峰值的一成多。第二章Compute (SM) Throughput的15.98 %是全程汇总结果,两者都说明SM执行资源没有吃满;逐窗口采样和全程汇总不要求数值完全相同 |
| SM ALU Pipe Throughput,0 到 100 % | ALU实际执行的指令数,除以同一窗口内ALU持续吃满时能执行的指令数,再乘100 %,得到ALU的指令吞吐达到自身峰值的比例。整数加法、逻辑和比较等指令走ALU管线 起步后升到约12 %的平台,末尾下降,与本kernel持续计算数组下标和循环条件的代码相呼应。这里的12 %说的是指令吞吐达到ALU自身峰值的比例;第二章ALU的11.84 %数的是管线有活动的周期占比,两项分别按指令数和活跃周期数计算 |
| SM FMA Light Pipe Throughput,0 到 100 % | FMA Light实际执行的指令数,除以同一窗口内该SMSP的FMA Light管线持续吃满时能执行的指令数,再乘100 %,得到这条管线的指令吞吐达到自身峰值的比例。这条管线处理FP32、FP16浮点算术 中段约1.4 %,表示FMA Light的指令吞吐只达到自身峰值的约1.4 %,曲线贴近底线。本kernel求和用的FADD是单精度浮点加指令,它的执行次数远少于整数和地址计算指令,FMA Light没有吃满 |
| SM FMA Heavy Pipe Throughput,0 到 100 % | FMA Heavy实际执行的指令数,除以同一窗口内该SMSP的FMA Heavy管线持续吃满时能执行的指令数,再乘100 %,得到这条管线的指令吞吐达到自身峰值的比例。除浮点算术外,这条管线还处理IMUL整数乘法、IMAD整数乘加等指令 中段约6.8 %,Light约为1.4 %;两个百分比分别以各自管线的峰值为基准。kernel计算全局下标、访存地址以及求余展开时都会用到整数乘加,所以Heavy的活动也包含整数计算。第二章FMA Heavy的8.82 %数的是管线有活动的周期占比,与这里按执行指令数统计的分子不同 |
| SM Tensor Pipe Throughput,0 到 100 % | 分子是采样窗口内SM内部Tensor管线实际处于活跃状态的周期计数,分母是该管线在所属SM的全部流逝周期内持续满负荷时对应的活跃周期计数,相除再乘100 %,得到Tensor管线活动达到持续峰值的比例。矩阵乘加等Tensor指令走这条管线 本kernel没有Tensor运算,采样值全为0,曲线全程贴底 |
DRAM分组:
DRAM就是GPU的显存。这一组看显存的数据传输速度,读、写分别表示数据从显存送出、向显存写入。下面的百分比都把实际速率与显存对应方向的峰值速率相比。
| 指标 | 说明 |
|---|---|
| DRAM Throughput,0 到 100 % | 先分别计算显存各组成指标的实际吞吐,再除以各自对应的持续峰值并乘100 %,最后取最高值,得到显存侧最忙组成指标相对自身峰值的利用率。这里量的是显存和显存控制器的数据传输 中段大约23 %,与第二章全程DRAM Throughput的23.23 %接近,说明显存侧最忙的组成指标也没有接近峰值。显存吞吐未满仍可能有访存等待:每秒搬了多少字节,和某条读取请求要等多久才能拿到数据,是两个不同的量 |
| DRAM Read Bandwidth,0 到 100 % | 在一个采样窗口里,从显存读出的字节数除以窗口时长,得到显存读速率;再用这个速率除以显存读方向的峰值速率并乘100 %,得到显存读速率达到读方向峰值的比例。两个速率都可以用字节/秒表示 中段约23 %,说明读取速度达到显存读峰值的约两成多。输入数组的数据如果已经在L1或L2中找到,就不需要再从显存读取,也不会增加本行的读流量。第二章Memory Chart里的显存读流量约67.12 MB,是整个kernel期间的总量;本行则把读活动按采样窗口分开显示 |
| DRAM Write Bandwidth,0 到 100 % | 在一个采样窗口里,写入显存的字节数除以窗口时长,得到显存写速率;再除以显存写方向的峰值速率并乘100 %,得到显存写速率达到写方向峰值的比例。它与上一行分别以写、读方向的峰值为基准 多数窗口接近0,间或出现短小的凸起,总体远低于读方向。序章里每个block只写回一个float,第二章Memory Chart里的显存写流量约2.05 MB,也远小于读流量。图上的凸起反映显存写活动在对应采样窗口中的增加 |
L2 Cache分组:
| 名字 | 含义 |
|---|---|
| L2 Throughput,0 到 100 % | 对L2内部处理请求和传输数据的各条通路,分别用实际处理速率除以该通路的持续峰值速率,再乘100 %,取其中最高值,得到L2最忙通路相对自身峰值的利用率。按周期计算速率时,使用L2单元自身的时钟周期 起步后升到约82 %的平台,末尾回落。这表示L2最忙的通路达到自身峰值的八成多,与第二章全程L2 Cache Throughput的80.44 %相近。显存约23 %则是显存相对自身峰值的水平;两个百分比分别反映两级硬件各自距离满负荷还有多远 |
| L2 Hit Rate,0 到 100 % | 统计这一窗口里,向L2查询的sector有多少已经能在L2中找到数据。用命中的sector数除以请求查询的sector数,再乘100 %,就是命中率 中段约89 %,可以读作每查询100个sector,大约89个在L2中找到数据,其余约11个需要继续向下一级存储请求。第二章的全程命中率是87.53 %。病态一按列读取时,相邻block会用到同一个sector里的不同float,前一个请求把数据带进L2,后续请求就有机会命中。因此,L2要处理很多请求,显存却不必为每个请求重新读一次数据。末尾的请求数量要结合L2 Throughput判断,即使命中率接近100 %,也只说明剩余请求多数命中 |
L1 Cache分组:
| 名字 | 含义 |
|---|---|
| L1 Throughput,0 到 100 % | 逐条计算L1TEX各组成通路在这一窗口里的"实际处理速率 ÷ 对应通路的持续峰值速率 × 100 %",再取最高百分比,得到L1TEX最忙通路相对自身峰值的利用率。计算每周期处理速率时,用的是L1TEX自身在窗口内的全部流逝周期,包括通路没有工作的周期 中段约57 %,采样最大值约63 %。本行来自L1TEX的实时采样指标,第二章L1/TEX Cache Throughput的92.45 %来自另一项全程采集指标。两处界面名称相近,解读时要分清各自使用的指标和采集方式。本图能说明L1TEX中段持续有访存工作;判断整条内存链路的瓶颈,还要结合L2和显存各自的吞吐以及第二章的流量证据 |
| Writeback Throughput,0 到 359.29 cycle | 每个采样窗口里,分别数各SM内部L1TEX的数据返回通路有多少个L1TEX时钟周期在工作,再对这些L1TEX单元取平均。数据返回通路负责把读回的数据交还给SM的寄存器 中段约300周期,最大值359.29周期。300的意思是每个L1TEX的数据返回通路,在这个窗口里平均有约300个自身时钟周期处于活跃状态。本kernel的全局读和共享读持续产生返回数据,中段因此形成平台。这个值汇总了窗口内多笔请求的数据返回活动 |
| Hit Rate,0 到 100 % | 统计这一窗口里,向L1TEX缓存查询的sector中有多少命中,再用命中的sector数除以请求查询的sector数并乘100 %,得到L1TEX这一层的sector命中率。与L2 Hit Rate的算法相同,区别是查询和命中都发生在L1TEX这一层 曲线几乎贴底,运行中段通常低于0.1 %,全程指标为0.080881 %,说明请求很少直接在L1TEX中找到数据。本kernel的全局读中,相邻线程的地址相隔32 KB,同一warp内难以合并;不同block之间虽然可能复用同一个sector,却不保证被安排到同一个SM、共用同一个L1。L2由所有SM共享,因此L1命中很少与L2命中较多并不矛盾 |
| Wavefronts (Data),0 到 850.93 | 每个采样窗口里,先数每个L1TEX的数据管线处理了多少个wavefront,再对各L1TEX单元取平均。一个wavefront是一份能在L1TEX数据管线的一个时钟周期内处理完的工作;一条访存请求可能拆成多份wavefront,分别占用多个管线周期 中段约800,最大值850.93,起步上升、末尾下降。800表示每个L1TEX在这个窗口里平均处理约800份wavefront工作。多单元取平均后,结果可以带小数。计数里包含正常访存所需的wavefront,也包含bank conflict导致的额外wavefront;第二章已把多余的524,288个wavefront定位到两条LDS.128共享内存加载指令 |
这张时间线补上了全程平均值里看不到的起步、中段和收尾过程。中段每个SM约驻留46个warp,每个SM活跃周期却平均只执行约0.57条warp级指令,说明驻留充足没有换来高指令执行速率。L2吞吐约82 %、显存约23 %,分别表示两级硬件相对自身峰值的利用程度,与第二章看到的内存链路压力分布相呼应。读图时,先认清每行统计的是哪个硬件单元,再看分子数的是指令、字节、sector还是活跃周期,以及分母用的是什么;同样叫百分比,回答的问题也可能完全不同。
Compute Workload Analysis
这一节分析SM在整个kernel执行期间的计算资源利用情况,主要看指令执行与发射速率,以及各条执行管线的利用率。
IPC,instructions per cycle,也就是每周期执行或发射多少条指令,这里仍按warp级指令计数。每个SM有4个SMSP,每个SMSP每周期最多发射一条,因此每SM每周期最多发射4条;发射侧IPC除以4,就得到发射槽利用率。
下面的数据表有Executed Ipc Elapsed、Executed Ipc Active和Issued Ipc Active三个IPC指标。名称里的Executed表示执行,Issued表示发射,用来区分分子数的是已执行的指令还是发射出去的指令。指标名末尾的Elapsed和Active用来区分分母。Elapsed使用SM在整个kernel执行期间流逝的全部时钟周期,Active只使用其中至少有一个warp驻留的活跃周期。这两种周期都按SM自身的时钟统计;warp驻留期间即使正在等数据或等同步,也会计入SM活跃周期。本报告每SM平均活跃437,992.54个周期,平均流逝442,680.22个周期,两者相除约为0.9894。同一项指令计数,用活跃周期作分母得到的速率,乘0.9894就能换成流逝周期口径。第二章的443,927是GPU层的流逝周期数,本节的IPC统一使用SM层的周期数。
基本数据表
ncu说明为:对流式多处理器(SM)计算资源的详细分析,包括每周期执行指令数和各计算管线利用率,利用率非常高的管线有可能限制整体性能。
这一节的5项在界面上分左右两列,左列三个是IPC,右列两个是百分比,下表先左列后右列:
| 项 | 值 | 说明 |
|---|---|---|
| Executed Ipc Elapsed | 0.56 inst/cycle | 每SM的warp级执行指令数,除以该SM在kernel期间流逝的全部时钟周期数,得到每SM每个流逝周期平均执行的指令条数 20,488,192 ÷ 82 ÷ 442,680.22 ≈ 0.5644,界面显示0.56,表示每SM每个流逝周期平均执行约0.56条指令 |
| Executed Ipc Active | 0.57 inst/cycle | 每SM的warp级执行指令数,除以该SM有warp驻留的活跃周期数,得到每SM每个活跃周期平均执行的指令条数 20,488,192 ÷ 82 ÷ 437,992.54 ≈ 0.5705,界面显示0.57。剔除SM没有warp驻留的周期后,分母略小,所以比上一行略高 |
| Issued Ipc Active | 0.57 inst/cycle | 每SM的warp级发射指令数,除以该SM的活跃周期数,得到每SM每个活跃周期平均发射的指令条数 20,512,745 ÷ 82 ÷ 437,992.54 ≈ 0.5711,界面也显示0.57。发射数比执行数多24,553条,约0.12 % |
| SM Busy | 14.28 % | 用SM指令处理的实际吞吐除以对应持续峰值,再乘100 %,得到SM指令处理活动相对持续峰值的利用率;统计范围是SM有warp驻留的活跃周期 本报告为14.28 %,与Issue Slots Busy相同。它量的是SM的指令处理活动 |
| Issue Slots Busy | 14.28 % | 实际发射指令数,除以SM活跃周期内全部可用发射槽数,再乘100 %,得到发射槽中实际被使用的比例。每SM每周期有4个发射槽 0.5711 ÷ 4 ≈ 14.28 %;若把SM空闲周期也计入分母,再乘0.9894,约为第二章的14.13 % |
这五个值要把执行与发射分开读:执行侧每SM每活跃周期约0.5705条,发射侧约0.5711条;后者除以4个发射槽,得到14.28 %。也就是说,平均约85.72 %的发射机会没有用上。原因可能是驻留的warp太少,也可能是warp已经驻留,但下一条指令所需的数据或同步条件还没满足;满足这些条件、随时可以发射的warp,报告称为eligible,也就是合格warp。
表格底下还挂着一条OPT警告,规则名叫Low Utilization,它说:所有计算管线都没吃满,要么是本workload非常小,要么是每个调度器发射的warp不够多,可以到Launch Statistics和Scheduler Statistics里看进一步细节。Est. Local Speedup是88.04 %。
这里的Local表示估算范围。不带Local的Est. Speedup估计整个kernel的运行时间可以减少多少;Est. Local Speedup针对规则关注的局部问题,例如某条管线还剩多少利用空间。评估优化建议时,要先分清估算范围。本条的88.04 %来自100 % − 11.96 %;图中指标表给出的ALU管线利用率原值为11.9634,保留两位小数就是11.96 %。ALU是这条规则比较的计算管线中最忙的一条,这个利用率按SM活跃周期统计。规则在这个利用率低于20 %时触发,本报告满足条件。剩余的88.04 %表示这套估算中尚未利用的份额,实际加速效果需要修改后重新测量。规则建议检查启动规模和调度情况。PM Sampling已经看到运行中段每SM约驻留46个warp,接近48个的硬件上限,因此本例可以先检查这些驻留warp为什么仍发不出指令。
Pipe Utilization 图
展开section,Pipe Utilization图把各条管线画成横条。左右面板的横轴都是利用率,范围为0到100 %,一行对应一条管线。横条不直接标数字,下面的读数来自悬停提示;显示值保留两位小数。管线可以同时处理多条尚未完成的指令。左面板统计管线活动的周期,右面板统计执行的指令,两边的百分比分别对照各自峰值读取。
左面板标题为Pipe Utilization (% of active cycles)。分子数管线有活动的周期,分母以该管线所属SM的活跃周期为基础,并按该管线持续满负荷的水平换算成百分比。SM有warp驻留时,ALU或FMA可能暂时没有指令要处理,因此管线的活跃周期数可以远少于SM活跃周期数。各行从上往下是ALU 11.96 %、FMA 5.17 %、FP64 0 %、Tensor (All) 0 %、Tensor (FP) 0 %、Tensor (INT) 0 %。ALU的活动主要来自整数加法、逻辑、比较和下标计算。FMA则是GA10x上覆盖FMA Light、FMA Heavy两条物理管线的汇总口径,既包含浮点算术,也包含Heavy承担的整数乘加。第二章FMA的5.11 %与FMA Heavy的8.82 %覆盖范围重叠,而且各自的峰值不同。本kernel没有双精度和Tensor运算,所以左面板后四行都是0。
右面板标题为Pipe Utilization (% of peak instructions executed)。这里改数执行过的指令:实际指令数除以"所属SM或SMSP的活跃周期数 × 对应管线每周期的指令吞吐峰值",再乘100 %。它表示一条管线达到自身指令吞吐峰值的程度。右面板从上往下有数值的七行是LSU 16.15 %、ALU 11.96 %、XU 8.76 %、ADU 4.38 %、FMA 4.07 %、Uniform 0.09 %、CBU 0.05 %;其余FMA (FP16)、FP64 (DMMA)、FP64 (FP64)、TEX、Tensor (FP)、Tensor (INT)都是0。两个FP64条目分别对应双精度矩阵运算和普通双精度指令,Tensor的FP、INT分别表示浮点和整数矩阵运算,本例都没有使用。
右面板最忙的是处理访存指令的LSU,16.15 %乘SM活跃周期与流逝周期之比0.9894,约为第二章的15.98 %。左面板最忙的是ALU的11.96 %,Low Utilization规则用的就是这一项。ALU两边读数相同,FMA则分别为5.17 %和4.07 %,对应的分子分别数周期和指令。XU仍是第二章介绍过的超越函数与类型转换单元,本例的I2F整数转浮点、MUFU.RCP求倒数、F2I浮点转整数来自病态三的求余展开。ADU支持分支地址处理和block同步,本kernel执行了720,896条BAR同步指令,是需要结合代码分析的一部分。Uniform是warp共用输入输出的标量通路,CBU处理汇聚屏障等指令;本行CBU按执行指令数和SM活跃周期计算,得到0.05 %;第二章那一项只计至少有一个线程满足执行条件的CBU指令,并使用流逝周期及该指标对应的峰值,得到0.20 %。两项各有自己的计数条件和分母。
Compute Workload Analysis给出的全程结果与PM Sampling相呼应:驻留的warp很多,SM执行和发射指令的速率却低。每SM每活跃周期只发射约0.57条,四个发射槽的利用率为14.28 %;各条执行管线也远未吃满。接下来沿调度器这一层,查看驻留warp里究竟有多少已经准备好发射。
Scheduler Statistics
这一节统计warp调度器管理、选择和发射warp的情况,通过驻留数量、合格数量和实际发射率,判断调度器有没有充分利用发射机会。每个SMSP有一个调度器,整卡共82 × 4 = 328个,每个最多管理12个驻留warp。下面表中的三个指标名称分别是Active Warps Per Scheduler、Eligible Warps Per Scheduler和Issued Warp Per Scheduler。Per Scheduler表示按每个调度器统计;Active Warps指已驻留的warp,Eligible Warps指其中下一条指令已具备执行条件的warp,Issued Warp指实际被调度器选中并发出指令的warp。调度器可以让多个warp交替发射:一个warp等数据时,另一个准备好的warp继续执行,这就是延迟隐藏,它把等待与其它warp的工作重叠起来。如果驻留的warp都没有准备好,调度器就只能空过这个发射机会。
基本数据表
ncu说明为:汇总调度器的指令发射活动。每个调度器从自己管理的驻留warp中检查哪些已经准备好,再选出一个发射;没有合格warp时,发射槽空着。大量空槽说明其它warp的工作没有充分盖住等待。本卡每个调度器每周期最多发射一条指令,按这个上限读本节数据即可。
这一节的5项在界面上分左右两列,左列三个是每调度器的warp数,右列两个是周期占比,下表先左列后右列。这里按SMSP的时钟数周期,平均数和周期占比以SMSP有warp驻留的活跃周期为统计范围。
| 项 | 值 | 说明 |
|---|---|---|
| Active Warps Per Scheduler | 11.42 warp | 在SMSP活跃周期里,平均每个调度器管理多少个驻留warp,原值11.421472 11.421472 ÷ 12 ≈ 95.18 %,说明驻留数量接近上限。正在等待的warp也计入这个平均数 |
| Eligible Warps Per Scheduler | 0.28 warp | 在SMSP活跃周期里,平均每个调度器有多少个warp已经准备好发射,原值0.278029 0.278029 ÷ 11.421472 ≈ 2.43 %,说明平均驻留数量中,合格的部分很少;每个周期按整数计数,跨周期平均后得到0.28 |
| Issued Warp Per Scheduler | 0.14 warp | 在SMSP活跃周期里,平均每个调度器每周期实际发射多少个warp的指令,原值0.142769 本卡每次选中一个warp发射一条指令,1 ÷ 0.142769 ≈ 7.0,即每个调度器平均约7个活跃周期才发射一条 |
| No Eligible | 85.72 % | 没有合格warp的SMSP活跃周期数,除以SMSP活跃周期总数,再乘100 %,得到调度器因没有合格warp而空过发射机会的周期占比 85.72 %的活跃周期没有可发射的warp。各调度器的结果在85.52 %到85.85 %之间,说明这种低发射率普遍存在 |
| One or More Eligible | 14.28 % | 至少有一个合格warp的SMSP活跃周期数,除以SMSP活跃周期总数,再乘100 %,得到调度器有warp可发射的周期占比 与No Eligible互补,85.72 % + 14.28 % = 100 %。本卡每个合格周期能使用一个发射槽,因此与前面的发射槽利用率对应,但统计层级分别是SMSP和SM |
这五个值连起来读:每个调度器平均有11.42个驻留warp,但每周期平均只有0.28个已经准备好,85.72 %的活跃周期里一个合格warp都没有。驻留数量接近上限,发射机会却大部分空着,说明等待没有被其它warp的工作充分隐藏。
表格底下还挂着一条OPT警告,规则名叫Issue Slot Utilization,它说:每个调度器本来每个周期能发出一条指令,本workload却平均每7.0个周期才发出一条,可能让硬件资源没用满、性能不够理想。12个warp的上限里平均有11.42个活跃、每周期只有0.28个合格;要增加合格warp,得避免各warp执行时长差太多造成负载不均,减少停顿也有帮助。Est. Local Speedup是19.56 %。
这条规则先算空槽份额:100 % − 14.28 % = 85.72 %。随后用Compute与Memory Throughput中较高的80.44 %约束估算,取85.72 %与100 % − 80.44 %两者中较小的值,得到19.56 %。这个19.56 %是规则针对当前计数器数据给出的估算。警告下方的关键指标表列出发射率原值0.142769,与上表Issued Warp Per Scheduler对应。倒数1 ÷ 0.142769 ≈ 7.0,给出平均每个调度器约7个活跃周期发射一条指令的结果。
Warps Per Scheduler 图
图的横轴是warp数,范围为0到16。五条横条依次是硬件上限12、本次配置的理论上限12、实际活跃11.42、合格0.28、实际发射0.14。前两条相等,说明启动配置没有把驻留上限压低;实际活跃数也接近12,合格和发射两条却很短。图中最明显的差距发生在"已经驻留"到"准备好发射"之间。
Scheduler Statistics把发不出指令的原因收窄到了warp状态:数量充足,但准备好的warp太少。平均每个调度器约7个活跃周期才发射一条指令,85.72 %的活跃周期没有合格warp;具体在等什么,要看这些warp各自的停顿原因。
Warp State Statistics
这一节按warp所处的状态统计周期,展示各类等待分别占多少。一个warp可能在等数据、等同步、等队列腾位,也可能已经准备好、只是调度器选了别的warp。ncu把这些状态分别统计;被选中并发出指令的状态叫Selected。这里统计warp累计周期,时钟仍是warp所在SM/SMSP的时钟。例如两个warp各等待3个周期,累计就是6个warp周期,即使两者的等待发生在同一段3周期里。把某种状态累计的warp周期数除以发射指令数,才得到图上"每发射一条指令平均摊到多少周期"的值。多个warp可以同时等待,要注意区分累计warp周期占比与kernel实际运行时间占比。表里的CTA是cooperative thread array,也就是一个block;CTA barrier要求同一个block内的线程在同步点会合。状态图中的Stall Membar指等待内存屏障,它处理访存先后顺序要求,和block内线程是否到齐不同。
基本数据表
ncu说明为:统计kernel执行期间warp所处的各类状态,分析warp为什么能或不能发射下一条指令。每条发射指令摊到的warp周期越多,就越需要其它准备好的warp提供工作来隐藏等待。分析停顿时,重点看它是否让调度器持续发不出指令。本例调度器有85.72 %的活跃周期发不出指令,所以值得分析占比最大的停顿。
说明底下是4个数据,界面上左右两列各两项,两条讲周期、两条讲线程数,下表先左列后右列:
读后两项前,先看归并循环里的if:条件成立的线程做加法,其余线程跳过这次加法。编译器会把各线程的条件判断结果保存为真或假,汇编里把这种结果叫谓词,后续指令据此决定哪些线程实际参与操作。例如一个warp的32个线程都还活跃,其中只有16个满足加法条件,这条指令就计32个活跃线程、16个实际参与操作的线程。后两项分别统计这两种数量的全程平均值。
| 项 | 值 | 说明 |
|---|---|---|
| Warp Cycles Per Issued Instruction | 80.00 cycle | 全部驻留warp累计的活跃周期数,除以warp级发射指令总数,得到每发射一条warp级指令平均摊到的warp周期数,原值79.999944 每条发射指令平均摊到约80个warp周期。这80个warp周期由多个warp的驻留周期累加而来;按每个调度器的时钟统计,实测平均约7个活跃周期发射一条指令 |
| Warp Cycles Per Executed Instruction | 80.10 cycle | 同一份warp累计活跃周期数,改除以warp级执行指令总数,得到每执行一条warp级指令平均摊到的warp周期数,原值80.095815 执行数比发射数少24,553条,分母略小,所以由80.00增到80.10 |
| Avg. Active Threads Per Warp | 31.94 | 把每次执行warp级指令时的活跃线程数累计起来,除以warp级执行指令总数,得到每条指令平均有多少个活跃线程 654,352,384 ÷ 20,488,192 ≈ 31.94;满足执行条件的线程数见下一行 |
| Avg. Not Predicated Off Threads Per Warp | 25.79 | 每次先数有多少活跃线程满足这条指令的执行条件,把这些线程参与的次数累计起来,再除以warp级执行指令总数,得到每条指令平均实际参与操作的线程数 528,400,384 ÷ 20,488,192 ≈ 25.79。与31.94相差约6.15,表示平均每条指令有约6.15个活跃线程因执行条件不满足而跳过操作 |
前两个数说明每条发射或执行指令摊到的warp累计周期,后两个数说明每条warp级指令有多少线程参与。活跃线程平均31.94个,按执行条件筛选后,实际参与操作的平均有25.79个。第二章Branch Efficiency为100 %,描述分支跳转是否一致;这里进一步统计每条指令有多少线程满足执行条件并参与操作。
以归并循环里的浮点加指令FADD为例:它执行524,288次,每次统计32个活跃线程,共16,777,216条线程指令;其中满足执行条件、实际参与加法的只有2,088,960条,比例约12.45 %。回到代码,8轮里实际做加法的线程数是128、64、32、16、8、4、2、1,加起来255;总的线程轮次是256 × 8 = 2,048,255 ÷ 2,048 ≈ 12.45 %。报告和源码在这里对上了,病态三的低效就体现在这些陪跑线程上。
表格下方有两条OPT警告和一条INF提示。第一条Barrier Stalls指出,每条发射指令平均摊到32.1个等待CTA barrier的warp周期,占80.0个总warp周期的约40.1 %。规则建议找到等待最多的同步位置,检查同步之前各warp的工作是否均衡;Est. Speedup为19.56 %。
关键指标表有两行:发射率0.142769,以及barrier等待原值32.0652。后者保留两位小数是32.07,32.07 ÷ 80.00 ≈ 40.1 %,就是这类等待占总warp累计周期的比例。回到本例,归并循环每一轮都执行 __syncthreads(),不同warp的有效工作量越来越不均衡;第二章采样表中,归并同步后那条BRA的barrier样本最多,说明这里值得优先分析。这项汇总包含其它同步位置的等待,具体贡献仍要按同步点分别检查。规则中"512线程以上考虑切成更小的组"的建议针对更大的block;本例每个block为256线程,分析重点是同步前代码的负载分配和逐行采样。
第二条Mio Throttle Stalls指出,每条发射指令平均摊到28.1个等待MIO指令队列腾位的warp周期,占80.0个总warp周期的约35.2 %。MIO是前面介绍过的内存输入输出单元,共享内存、特殊数学和动态分支等指令会经过相关队列。规则建议检查是否能用更少、更宽的共享内存加载减轻队列压力;Est. Speedup同样为19.56 %。
关键指标表第二行给出MIO队列等待原值28.1336,保留两位小数为28.13。28.13 ÷ 80.00 ≈ 35.2 %。这里统计warp在MIO指令队列入口等待空位的周期;等待操作结果的周期在后面的状态图中另行统计。第二章已经定位到两条LDS.128的bank conflict:每次读理想需要4个wavefront,实际需要8个。额外wavefront在L1TEX中串行处理,会延长请求的服务时间,并可能加重上游排队。MIO队列以指令为单位,后续L1TEX数据管线则以wavefront为工作单位。一次指令发射后,请求可以在L1TEX里拆成多个wavefront继续处理。规则建议用更少、更宽的加载,本例编译器已经把八次float读合成了两条128位加载,但冲突仍在。加载宽度之外,还需要检查访问地址的布局;同时,MIO还有其它指令来源,要结合Source页定位,才能判断各段代码的贡献。
两条OPT都显示19.56 %,也与前面Issue Slot Utilization的数值相同,是因为估算都受到80.44 %这个吞吐指标的约束。三项涉及的等待可能互相关联,不能把三个19.56 %相加;前面的Issue Slot Utilization还是Local估算,两条停顿警告则估计整个kernel的运行时间改善。
第三条INF提示叫Warp Stall,没有Est. Speedup。它引向第二章读过的Warp Stall Sampling (All Samples):本节按原因汇总,那张表按指令定位,配合阅读才能把等待落回具体代码。
Warp State (All Cycles) 图
展开section,Warp State (All Cycles)图一行展示一种warp状态,按平均周期数从大到小排列。横轴是每发射一条指令摊到的该状态warp周期数,刻度为0到40;横条上的具体数值从悬停提示读取。图中也列出了Selected,即warp已经被选中发射的状态。
图上18行的数值和来历如下表所示,各行的占比是拿周期数除以80.00算出来的:
| 项 | 值 | 说明 |
|---|---|---|
| Stall Barrier | 32.07 cycle | 等同一个block的其它warp到达同步点,占40.1 % 本例归并每轮都同步,逐行采样也指向归并循环;这一行汇总了所有同步位置的等待 |
| Stall MIO Throttle | 28.13 cycle | 等MIO指令队列腾位,占35.2 % 共享内存冲突会延长访存服务,可能加剧上游排队;MIO也承接特殊数学和动态分支,要结合逐行信息判断来源 |
| Stall Short Scoreboard | 8.35 cycle | 等MIO操作的结果依赖,占10.4 %;记分板记录哪些结果还没准备好 常见来源是共享内存访问,也可能来自特殊数学和动态分支。这里等MIO操作的结果,上一行等MIO指令队列腾位 |
| Stall Wait | 3.23 cycle | 等待固定延迟指令的结果依赖,占总warp周期约4.0 % 前一条指令的结果还没准备好,当前指令又要使用这个结果,warp就不能继续发射。本例这一项明显小于Barrier和MIO Throttle |
| Stall Long Scoreboard | 2.65 cycle | 等待L1TEX访存操作的结果依赖,例如全局或本地内存读取,占总warp周期约3.3 % 病态一的全局加载可能造成这类等待。L2高命中率能减少需要下到显存的请求,具体等待位置仍要结合逐行采样分析;采样记录的是等待使用数据的指令,发起读取的LDG可能位于更前面 |
| Stall LG Throttle | 2.57 cycle | 等待本地、全局访存方向的指令队列腾出位置,占总warp周期约3.2 % 本例有八条展开后连续发射的LDG.E全局加载指令,是需要检查的来源。这里等的是队列入口,Long Scoreboard等的是已发出请求的结果 |
| Selected | 1.00 cycle | warp被选中并发出指令;Raw原值0.999997 每条发射指令平均对应约一个被选中的warp周期 |
| Stall Not Selected | 0.95 cycle | warp已经合格,但这个周期调度器选了另一个warp发射,占总warp周期约1.2 % 它说明准备好的warp之间存在选择。本例这一项较小,结合Eligible平均只有0.28,主要问题仍是准备好的warp不足 |
| Stall Drain | 0.23 cycle | warp执行EXIT后,等待尚未完成的访存操作结束,以便释放资源,占0.29 % EXIT是线程退出指令;本项记录退出阶段为收尾访存而等待的周期,具体位置可结合Source页判断 |
| Stall Math Pipe Throttle | 0.18 cycle | 等数学管线可以接收下一条指令,占0.23 % 本报告这类等待很少 |
| Stall Dispatch Stall | 0.17 cycle | 指令已备好,派发环节因为冲突或事件压着不发,占0.22 % 这里统计的是派发环节的等待,本报告占比很小 |
| Stall No Instruction | 0.13 cycle | 等被选中取指,或等指令缓存缺失处理,占0.16 % 本报告取指等待占比较小 |
| Stall Branch Resolving | 0.10 cycle | 等分支目标解析及程序计数器更新,占0.13 % 它和同一warp内线程走不同路径造成的分支分歧是两件事 |
| Stall IMC Miss | 0.10 cycle | 等待立即数常量缓存的缺失处理,占总warp周期约0.13 %。IMC是immediate constant cache,用于访问指令中指定的常量数据 本例常量读取等待占比较小 |
| Stall Tex Throttle | 0.00 cycle | 等待纹理方向的指令队列腾出位置,原值0.000669周期 保留两位小数后显示0.00,本报告这类等待极少 |
| Stall Misc | 0.00 cycle | 未归入其它条目的硬件等待,原值0.000175周期,显示为0.00 本例占比很小 |
| Stall Membar | 0 cycle | 等内存屏障,处理访存顺序要求 0,本报告没有记录到这类停顿;block同步位置的等待看Stall Barrier |
| Stall Sleeping | 0 cycle | warp里全部线程都阻塞、让出或睡眠时记这项,由NANOSLEEP这类指令造成 0,本kernel没有让线程睡过 |
这18行里,Barrier与MIO Throttle合计60.20个warp周期,除以80.00约占75.2 %;再加其后四项,前六项约占96.2 %。按这个占比确定分析顺序,应先检查同步、共享访存以及相关指令队列。本报告18项原值相加为79.87,与总项80.00略有差异;显示值相加为79.86,又有舍入差异。计算占比仍沿用总项80.00,保留各项实测值。
这张图还可以和第二章的Warp Stall Sampling表交叉验证。采样表按指令收集停顿样本,其中barrier占37.6 %、MIO Throttle占35.5 %、Short Scoreboard占11.5 %;与本表相比,前三项的顺序相同,比例也接近。一边的分母是17,547笔采样,另一边的分母是累计warp周期,两表从不同统计角度指向相同的主要等待原因。
Warp State Statistics把等待原因展开了:Barrier的32.07和MIO Throttle的28.13合计占总warp周期约75.2 %,应优先检查同步和相关指令队列。80.00 ÷ 11.421472 ≈ 7.0,也把每条指令摊到的warp累计周期,与前面每个调度器约7个活跃周期发射一条的结果接了起来。具体修改哪个同步点或哪段访存,仍要结合Source页的逐行证据。
Instruction Statistics
这一节统计各类SASS指令的执行次数,展示kernel实际执行的指令组成。报告按opcode,也就是操作码分类:IMAD算一类,FADD算另一类,每类把所有对应指令的执行次数加起来。这就是指令混合统计,用来观察整数计算、浮点计算、访存和控制指令各占多少。
基本数据表
ncu说明为:统计执行过的SASS指令,按类型和次数展示指令混合。某类指令特别多,说明对应管线承担的工作较多;是否限制性能,还要结合Compute Workload Analysis的管线利用率判断。按操作码分类统计的执行次数,与执行指令总数采用的采集方法不同,遇到系统调用等情况时可能出现偏差;本报告21类指令的次数之和与执行总数相等。
说明底下是4个数据,界面上左右两列各两项,左列两个总数、右列两个平均值,下表先左列后右列:
| 项 | 值 | 说明 |
|---|---|---|
| Executed Instructions | 20,488,192 inst | 整个kernel执行的warp级指令总数,一条warp级指令计一次,不按32个线程重复计数 指令混合的占比与平均参与线程数都用20,488,192作分母;每条发射指令摊到的warp周期则用发射总数作分母 |
| Issued Instructions | 20,512,745 inst | 整个kernel发射的warp级指令总数 20,512,745 − 20,488,192 = 24,553,比执行数多约0.12 % |
| Avg. Executed Instructions Per Scheduler | 62,464 inst | 20,488,192 ÷ 328 = 62,464,每个调度器平均执行的warp级指令数 最少61,272条,最多65,026条;(65,026 − 61,272) ÷ 62,464 ≈ 6.0 %,说明各调度器的执行数量比较接近 |
| Avg. Issued Instructions Per Scheduler | 62,538.86 inst | 20,512,745 ÷ 328 ≈ 62,538.86,每个调度器平均发射的warp级指令数 比平均执行数多74.86条,正好对应总差额24,553 ÷ 328 ≈ 74.86 |
这四个值把全章反复使用的两个总数列到了一起:执行20,488,192条,发射20,512,745条。Compute Workload Analysis用它们除以SM周期算IPC;Warp State分别用它们作分母,计算每条指令摊到的warp周期和线程参与数量。
表格底下挂着一条OPT警告,规则名叫FP32 Non-Fused Instructions,它说:本kernel执行了0条融合和1,048,576条非融合的FP32指令,建议检查是否能把成对的非融合指令换成融合版本,并给出FP32环节50 %的优化空间,可以到Source页找FP32指令的位置。Est. Speedup是2.58 %。
这说的是融合乘加:FADD做加法,FMUL做乘法,FFMA一条指令计算a×b+c。存在合适的乘、加配对时,一条FFMA可以代替两条指令,编译器通常会尝试这种融合。本kernel的1,048,576条非融合FP32指令全是FADD:共享内存里的八个数逐次相加,共524,288条;树形归并中的FADD又执行524,288次。这两处都是纯加法,缺少与加法配对的乘法,因此这条融合建议在本例中用不上。规则用"非融合指令占比的一半"估算局部空间,本例是100 % ÷ 2 = 50 %,再乘图中指标表给出的FMA管线利用率5.16628 %,得到显示的2.58 %。这个估计的前提是代码中存在可融合的乘加组合,阅读这条建议时要先核对这一条件。
Executed Instruction Mix 图
表格底下是Executed Instruction Mix图,把执行过的指令按opcode分类画了出来:
一行一种opcode,按执行次数从大到小排,横轴是warp级指令条数,刻度到5,000,000;横条上不标数字。一共21行,行序与图完全一致,占比拿每行条数除以20,488,192;MUFU、LOP3、LEA、LDG、I2F、F2I六条并列524,288次,ULDC、SHF、CS2R三条并列65,536次,并列的按名字倒着排:
| 项 | 值 | 说明 |
|---|---|---|
| IMAD | 4,792,320 | 整数乘加,一条指令计算整数a × b + c,占全部执行指令约23.4 % 本例最多的一类,主要用于下标、地址及求余展开中的整数计算,GA10x上由FMA Heavy管线承担 |
| ISETP | 3,276,800 | 整数比较,把条件成立与否写进谓词寄存器,占16.0 % 循环条件、if判断、归并每轮的步长判断都靠它;后续指令利用这个结果决定哪些线程参与操作,走ALU管线 |
| IADD3 | 2,555,904 | 三输入整数加法,占12.5 % 地址计算里的加法、循环变量自增,走ALU管线 |
| MOV | 1,646,592 | 搬运操作数,占全部执行指令约8.0 % 本例用于初始化寄存器、给后续指令准备输入 |
| LDS | 1,187,840 | 共享内存读,占全部执行指令约5.8 % 归并中的两条LDS各执行524,288次,共1,048,576次;病态二的两条LDS.128各执行65,536次,共131,072次;收尾读取smem0再执行8,192次,合计1,187,840次。本行按warp级指令计数,访存请求数与wavefront数另行统计 |
| STS | 1,114,112 | 共享内存写,占5.4 % 病态一读回的八个数由八条STS各存一个、各65,536次;部分和算完由第九条STS写回,也是65,536次;归并循环里的那条执行524,288次 |
| FADD | 1,048,576 | 单精度浮点加,占5.1 % 本kernel全部的非融合FP32指令,来历见上面OPT那段 |
| BAR | 720,896 | block同步指令,占3.5 % 归并循环中的一条执行524,288次,另外三条各执行65,536次。指令次数能认出同步有多频繁,等待有多长还要看Stall Barrier和逐行采样 |
| BRA | 589,824 | 分支跳转,占2.9 % 归并循环中的那条执行524,288次,对应第二章Warp Stall Sampling表最上面那一行;另一条执行65,536次 |
| MUFU | 524,288 | 特殊函数指令,这里是求倒数MUFU.RCP,占2.6 % 它与I2F、F2I一起参与病态三的变量求余展开,这几步走XU单元;求余过程还包含整数乘加等指令 |
| LOP3 | 524,288 | 三输入位逻辑运算,一条对三个操作数做任意逻辑组合,占2.6 % 参与求余展开 |
| LEA | 524,288 | 地址计算指令,占2.6 % 归并循环里算共享内存的访存地址 |
| LDG | 524,288 | 全局内存读,占全部执行指令约2.6 % 八条展开的LDG.E各执行65,536次,对应病态一从input搬数的循环。每条指令先按globalIdx < n判断哪些线程需要读取,读取结果随后存入共享内存;非合并访问多出的sector来自这里 |
| I2F | 524,288 | 整数转浮点,占2.6 % 本次编译把变量求余展开成了包含类型转换和求倒数的指令序列,I2F是其中的整数转浮点步骤 |
| F2I | 524,288 | 浮点转整数,占全部执行指令约2.6 % 与I2F整数转浮点、MUFU.RCP求倒数一起参与变量求余的展开。这三类指令都走XU,求余展开的回代部分还包含整数乘法等操作 |
| S2R | 131,072 | 读特殊寄存器,占0.6 % 两条各65,536次,分别读blockIdx和threadIdx,kernel开头算下标用 |
| EXIT | 73,728 | 线程退出指令,占0.4 % 收尾时先让无需写回的线程退出,这条带条件的EXIT按warp计65,536次;每个block剩下的thread 0写回后再退出,又计8,192次,合计73,728次 |
| ULDC | 65,536 | 从常量内存读取数据到warp共用的统一寄存器,占全部执行指令约0.3 % 本例每个warp执行一次,8,192个block × 每block 8个warp = 65,536次。它属于准备常量数据的工作 |
| SHF | 65,536 | 移位运算,占0.3 % 算地址时把threadIdx左移5位,相当于乘32 |
| CS2R | 65,536 | 把特殊寄存器里的常量写进普通寄存器,占0.3 % 这里是把零常量写进寄存器当初始值 |
| STG | 8,192 | 全局内存写,占全部执行指令约0.04 % 本例只有一条STG,每个block由thread 0写回一个float,共执行8,192次。它对应序章if (threadIdx.x == 0)包住的output写回,也就是病态四 |
IMAD、ISETP、IADD3、MOV四类合计12,271,616条,占59.9 %,主要在算下标、算地址、比较条件和准备操作数。LDS、STS、LDG、STG四类合计2,834,432条,占13.8 %。访存类指令条数占比不高,仍可能制造很多sector、wavefront和等待,这正是本例值得读的地方。这里的59.9 %、13.8 %用全部执行指令数作分母;Compute Workload Analysis的ALU 11.96 %、LSU 16.15 %、XU 8.76 %则用各管线自己的峰值作分母。前者描述指令组成,后者描述管线利用率。21类指令的条数之和等于执行总数;表里的百分比逐项舍入后相加,可能与100 %略有差异。
Instruction Statistics说明本例指令以整数和地址计算为主,访存指令数量相对较少,却能产生大量sector、wavefront和等待。四个病态在不同指令上各有对应证据;FP32 Non-Fused规则给出的2.58 %估算,需要结合代码中的乘加配对条件评估。
NVLink Topology / NVLink Tables / NUMA Affinity
接下来三个section展示硬件互联与主机亲和关系。NVLink用于GPU之间的高速直连;NVLink Topology的说明是展示逻辑连接及其收发吞吐。本报告展开后只有一句The system does not have any NVLink connections.,表示没有检测到NVLink连接。
NVLink Tables的说明为:给出详细表格,列出每条NVLink的属性,界面上表格的小标题写的是Logical NVLink Properties;本机没有连接,也就没有属性可列,展开后还是上一节那句话:
NUMA Affinity有实际数据。NUMA是non-uniform memory access,非统一内存访问:主机内存按节点组织,设备访问不同节点的距离和代价可能不同。亲和性说明GPU与哪些逻辑CPU、哪个内存节点关系更近。逻辑CPU是操作系统能够分别调度的处理器编号;启用超线程时,一个物理核心可以对应多个逻辑CPU。ncu在NUMA ID Table中按GPU列出这些关系,一行对应一个GPU。
表里只有GPU 0,即NVIDIA GeForce RTX 3090。NUMA ID by CPU Affinity为0,CPU Affinity为0-31,表示报告列出的亲和逻辑CPU编号是0到31、对应0号节点;NUMA ID by Memory Affinity也为0,表示内存亲和节点同样是0。两列分别描述CPU和内存亲和关系。
这三个section没有给出性能建议。NVLink两节确认没有NVLink连接,NUMA表给出CPU与内存亲和节点;本kernel是否访问主机内存,要看实际流量。第二章Memory Chart的System Memory为0.00 B,才是本次没有记录到主机内存流量的证据。
Launch Statistics
这一节列出kernel的启动配置:grid有多少个block,每个block有多少线程,以及寄存器和共享内存用量。线程、寄存器、共享内存和硬件block数量都有各自的上限;每个block占用多少资源,决定SM能同时驻留多少个block。
基本数据表
ncu说明为:kernel启动配置的汇总。启动配置定义了网格的大小、网格划分成block的方式,以及执行这个kernel需要的GPU资源,选一套高效的启动配置能把设备利用率拉满。
这一节一共15项,界面分左右两列。下表先读左列,从Grid Size到Enabled TPC IDs,再读右列,从Function Cache Configuration到# TPCs,与截图顺序一致。
| 项 | 值 | 说明 |
|---|---|---|
| Grid Size | 8,192 | grid中的block总数 2^24个输入,每block负责256 × 8 = 2,048个数,2^24 ÷ 2,048 = 8,192,与序章的启动配置一致 |
| Registers Per Thread | 36 register/thread | 编译后每个线程使用36个寄存器,硬件分配还要向上取整 每warp需要36 × 32 = 1,152个,按每warp 256个的分配粒度向上取整为1,280个,相当于每线程40个。计算资源能装几个block时,按取整后的分配量计算 |
| Block Size | 256 | 每个block的线程数 256 ÷ 32 = 8个warp,所以每多驻留一个本例的block,就要为8个warp安排位置 |
| Threads | 2,097,152 thread | 全部线程数,8,192 × 256 = 2^21 224个数摊到221个线程上,每线程8个,搬数循环里那八条LDG.E就是这么来的 |
| Waves Per SM | 16.65 | 以当前配置下整卡能同时驻留的一批block为一波,grid总block数除以这一波的容量,得到整个grid相当于多少波的驻留容量 每SM最多6个block,整卡82 × 6 = 492个;8,192 ÷ 492 ≈ 16.65波。这个数按驻留容量折算,实际执行时有block完成就能补入新block;末尾不足一波时可能出现部分SM先空下来的尾效应 |
| Uses Green Context | 0 | 是否使用CUDA给任务划定部分GPU资源的Green Context机制 0表示本例没有使用 |
| # SMs | 82 SM | 这块GPU有82个SM 本章所有按SM取平均的算法,分母都是它 |
| Enabled TPC IDs | all | 本次启动可使用全部TPC,即前面介绍的每两个SM一组的硬件单元,没有被限制到其中一部分 |
| Function Cache Configuration | CachePreferNone | kernel对缓存与共享内存划分的偏好配置 CachePreferNone表示没有提出特别偏好。本次实际分给每SM的共享内存容量,单独列在Shared Memory Configuration Size中 |
| Static Shared Memory Per Block | 0 byte/block | 编译期静态申请的共享内存,0 本kernel的共享内存全是运行时动态申请的 |
| Dynamic Shared Memory Per Block | 8.19 Kbyte/block | 启动时动态申请的共享内存,每block一份 本次由kernel启动的第三个参数指定8,192字节,界面按十进制缩写显示为8.19 Kbyte |
| Driver Shared Memory Per Block | 1.02 Kbyte/block | 驱动为每block预留的系统开销,与kernel申请多少无关,Raw页原值1,024字节 容易漏看,算驻留上限时必须加上 |
| Shared Memory Configuration Size | 65.54 Kbyte | 本次每SM配给共享内存的容量,65,536字节,即64 KiB 界面使用十进制Kbyte,显示为65.54。驻留计算用原始字节数:65,536 ÷ 9,216 ≈ 7.11,可装7个block |
| Stack Size | 1,024 | 启动时配置的栈大小,Raw页和说明未标出单位,这里沿用界面值 本报告local load/store指令数都是0 |
| # TPCs | 41 | TPC是硬件里2个SM一组的单位,82 = 41 × 2 硬件信息,随设备而定,不随kernel变 |
这15个值给出了本次资源用量:8,192个block,每block 256个线程;每线程使用36个寄存器,分配时按每线程40个所在的档位计算;每block共享内存为8,192 + 1,024 = 9,216字节。把这些用量分别与每SM的资源总量相比,就能算出同时驻留的block上限。
Occupancy
这一节根据kernel的资源用量计算每SM的理论驻留上限,并与实测驻留数量比较。本例中,硬件最多容纳48个warp,若资源允许同时驻留6个本例的block,就是6 × 8 = 48个warp。驻留warp数除以48,得到的比例就是occupancy。它描述驻留数量;warp即使已经驻留,也可能正在等待,无法发射下一条指令。
基本数据表
ncu说明为:occupancy是每个SM的活跃warp数与其最大可驻留warp数之比。高occupancy不一定带来高性能;较低的occupancy会减少可用于隐藏延迟的warp数量。理论值与实测值差得很大时,应检查负载是否均衡。这里的"活跃"仍然是驻留,正在等待的warp也计入其中。
这一节共有8项,界面左列四项描述occupancy,右列四项分别给出资源允许的block数量上限,下表先左列后右列。
| 项 | 值 | 说明 |
|---|---|---|
| Theoretical Occupancy | 100 % | 按启动配置和资源限制算出的每SM最大驻留warp数,除以硬件的48个warp上限,再乘100 %,得到本次配置在资源上允许的最大warp驻留比例 本例允许48个warp,48 ÷ 48 × 100 % = 100 %,表示本次配置在资源上允许满驻,实测值见Achieved Occupancy |
| Theoretical Active Warps per SM | 48 warp | 按本次资源用量计算,每SM理论最多驻留48个warp 各资源允许的block数取最小值,再乘每block的8个warp,得到这48个 |
| Achieved Occupancy | 95.41 % | 实测每SM平均活跃warp数除以48,再乘100 %,得到实际平均驻留warp数占硬件上限的比例;这里的活跃仍表示驻留 45.795337 ÷ 48 × 100 % ≈ 95.406952 %,显示95.41 %,与100 %相差4.59个百分点;起步、block换入和收尾等阶段都可能出现暂未满驻的情况 |
| Achieved Active Warps Per SM | 45.80 warp | 每SM在自身活跃周期里平均驻留45.80个warp Scheduler Statistics按每调度器统计,使用原值11.421472 × 4 ≈ 45.686,与SM层的45.795337相差约0.24 %。两个层级的实测计数略有差异,这里共同说明驻留接近48个的上限 |
| Block Limit Registers | 6 block | 仅考虑寄存器容量时,每SM最多能驻留的block数 每warp分配1,280个,每block 8个warp,共10,240个;每SM有65,536个寄存器,65,536 ÷ 10,240 = 6.4,向下取整为6个block |
| Block Limit Shared Mem | 7 block | 共享内存给的驻留上限 每block 9,216字节,SM配了65,536字节,65,536 ÷ 9,216得7.11,取整7 |
| Block Limit Warps | 6 block | warp数给的驻留上限 SM最多48个warp,每block 8个,48 ÷ 8 = 6 |
| Block Limit SM | 16 block | 每SM硬件最多允许驻留16个block 本例还受寄存器和warp数量限制,只能驻留6个,达不到16个。资源上限中必须取最小值 |
四个block上限取最小值:寄存器允许6个、共享内存允许7个、warp数量允许6个、SM硬件允许16个,因此本例理论上最多驻留6个block,6 × 8 = 48个warp,理论occupancy为100 %。运行中旧block完成、新block补入,起步和收尾阶段也不一定满驻,所以实测为95.41 %。各SM的实测值从94.570542 %到96.292889 %,相差约1.7个百分点,说明驻留水平比较接近。
Impact of Varying 六图
展开section,六张Impact of Varying图分成上下两排,每排三张。横轴依次是每线程寄存器数、block线程数、每block共享内存用量;上排纵轴是occupancy百分比,下排是每SM驻留warp数,两排展示同一批理论结果。计算时固定其它条件,只改变横轴上的一个参数,按各项资源上限算出可驻留block数,再换成warp数和occupancy。圆点标记本kernel当前参数对应的理论结果。曲线达到100 %表示资源允许满驻;参数对运行时间的影响还要另行测量。
第一张横轴是Registers Per Thread,范围为0到256,每8个寄存器一格。按本例block大小,可驻留block数依次从6降到5、4、3、2、1,对应occupancy约100 %、83 %、67 %、50 %、33 %、17 %,所以曲线呈台阶状。寄存器按每warp 256个的粒度分配。本例每线程使用36个,一个warp需要36 × 32 = 1,152个,向上取整到1,280个,相当于每线程40个。使用量不超过40时仍在这一档;增到41时,分配量跳到每线程48个,每SM可驻留block数从6降到5。当前圆点位于36,仍在100 %平台上,使用量还能增加4个而不跳档。
第二张横轴是Block Size,范围为0到1,024,每32个线程一格。block很小时,每个block只有少数warp,而固定的共享内存用量最多允许7个block,所以32线程时只能驻留7个warp,7 ÷ 48 ≈ 14.6 %。线程数增多后,曲线在256、384、512、768等位置达到100 %,中间出现锯齿。对比192、224、256这三处:192线程是6个warp,装满48个warp需要8个block,但共享内存只允许7个,所以只能驻留7 × 6 = 42个warp,即87.5 %;224线程是7个warp,48 ÷ 7向下取整为6个block,也只能驻留42个warp;256线程是8个warp,6个block正好48个。锯齿来自资源限制与整块block必须一起驻留的约束,当前256的圆点位于满驻位置。
第三张横轴是Shared Memory Per Block,范围为0到101,888字节。每block用量增加,SM能容纳的block就逐档减少,曲线因此往下走台阶。本次每SM配置65,536字节共享内存,粗算能容纳6个block的分界为65,536 ÷ 6 ≈ 10,922字节;实际还要考虑分配粒度。当前圆点为每block 9,216字节,仍在100 %平台上。曲线在49,152字节附近降到0,对应本次配置采用的默认单block 48 KiB上限。要申请更大的单block份额,需要程序显式启用更大共享内存用量。每SM总容量限制同时能装几个block,单block上限限制一个block本身能否按当前配置启动,两者要分开看。
下排三张图的标题与横轴对应上排,纵轴改为每SM驻留warp数。第一、三张标0、20、40、60、80,第二张标0、12、24、36、48;其中48个warp都对应100 % occupancy。寄存器图中上排的100 %、83 %、67 %、50 %、33 %、17 %,换到下排就是48、40、32、24、16、8个warp。
三张图的圆点都压在满驻那一线上,横坐标与上排一一对应:36个寄存器、256、9,216字节,纵坐标都是48个warp。
Occupancy说明本例的资源用量允许满驻:寄存器和warp数量都把上限限制在6个block,正好48个warp;理论值100 %,实测95.41 %。这与Scheduler Statistics的结论一致,应优先解决驻留warp的等待。
GPU and Memory Workload Distribution
这一节比较同类硬件单元之间的活跃周期,查看工作是否集中在少数单元上。例如比较82个SM的最小值和最大值,能看出各SM有warp驻留的时间是否接近;L2和显存也分别在各自同类单元之间比较。SM、L2、DRAM分别使用各自的时钟。
基本数据表
ncu说明为:分析workload在各硬件单元上的分布。本报告列出SM、SMSP、L1、L2、DRAM五类单元的活跃周期,用来比较同类单元之间的活动是否均衡。
说明下方左右两列各五项,单位都是cycle。名称带Average的是同类单元的平均活跃周期数,带Total的是同类单元的流逝周期总数。活跃周期只计入该单元满足活动条件的周期,流逝周期则包含空闲时段;两者还一个取平均、一个取总和。所以下表按界面先左列后右列读,比较时必须把同一类单元、同一种周期统计配起来。
| 项 | 值 | 说明 |
|---|---|---|
| Average SM Active Cycles | 437,992.54 cycle | 把82个SM各自有warp驻留的时钟周期数相加,再除以82,得到每SM平均有多少周期保持warp驻留 平均437,992.54周期,包含warp等待期间。Compute Workload Analysis中名称带Active的两个IPC指标用它作分母 |
| Average L2 Active Cycles | 427,849.06 cycle | 把各L2 slice的活跃周期数相加,再除以slice数量,得到每个L2 slice平均活跃多少周期;周期属于L2自身时钟 本例平均427,849.06周期。与同类L2的流逝周期相比,活跃比例约98.45 % |
| Average DRAM Active Cycles | 720,533.33 cycle | 把各显存通道的活跃周期数相加,再除以通道数量,得到每个显存通道平均活跃多少周期;周期属于显存侧时钟 平均720,533.33周期。显存时钟约为SM的七倍,活跃比例要用显存自己的活跃周期数除以流逝周期数 |
| Total L1 Elapsed Cycles | 36,299,778 cycle | 全部L1TEX单元在kernel期间流逝周期数的总和,包括各单元没有活动的周期 本报告与Total SM Elapsed Cycles同为36,299,778,但两行仍分别描述L1TEX和SM |
| Total SMSP Elapsed Cycles | 145,199,112 cycle | 328个SMSP在kernel期间流逝周期数的总和 145,199,112 ÷ 328 ≈ 442,680.22周期,是每SMSP的平均流逝周期;它与SM层的平均值相同,因为本卡每SM有4个SMSP,二者使用同一时钟 |
| Average L1 Active Cycles | 437,992.54 cycle | 全部L1TEX单元的活跃周期总数除以单元数量,得到每个L1TEX单元平均活跃多少周期 本报告与SM层同为437,992.54周期。它记录L1TEX这一层的活动周期 |
| Average SMSP Active Cycles | 438,043.79 cycle | 全部SMSP各自有warp驻留的活跃周期总数,除以328个SMSP,得到每SMSP平均有多少周期保持warp驻留 平均438,043.79周期,比SM层的平均值高约0.0117 %。计算SMSP层的指标沿用本行实测值 |
| Total SM Elapsed Cycles | 36,299,778 cycle | 82个SM在kernel期间流逝周期数的总和,包括没有warp驻留的周期 36,299,778 ÷ 82 ≈ 442,680.22周期,是前面计算Executed Ipc Elapsed用的平均值;活跃总数35,915,388除以本行总数约为0.9894 |
| Total L2 Elapsed Cycles | 20,860,896 cycle | 全部L2 slice在kernel期间流逝周期数的总和 L2活跃总数20,536,755 ÷ 流逝总数20,860,896 ≈ 98.45 %,两份总数必须都来自L2这一层 |
| Total DRAM Elapsed Cycles | 37,220,352 cycle | 全部显存通道在kernel期间流逝周期数的总和 37,220,352 ÷ 12 = 每通道3,101,696周期;显存活跃总数8,646,400除以本行总数约为23.23 % |
Workload Distribution 分布表
展开后的表格叫Workload Distribution,一行对应一类硬件单元。Average是这一类单元的平均活跃周期,Min与Max是其中最小、最大的活跃周期,Sum是全部同类单元的活跃周期之和;四列用的都是这一行硬件单元自身的时钟周期。
| 项 | Average | Min | Max | Sum |
|---|---|---|---|---|
| SM Active Cycles | 437,992.54 | 435,302 | 441,248 | 35,915,388 |
| SMSP Active Cycles | 438,043.79 | 435,339 | 441,146 | 143,678,364 |
| L1 Active Cycles | 437,992.54 | 435,302 | 441,248 | 35,915,388 |
| L2 Active Cycles | 427,849.06 | 427,556 | 429,493 | 20,536,755 |
| DRAM Active Cycles | 720,533.33 | 719,184 | 721,712 | 8,646,400 |
用Sum除以Average,可以反推参与统计的单元数量:SM为35,915,388 ÷ 437,992.54 ≈ 82,SMSP为143,678,364 ÷ 438,043.79 ≈ 328,L2为20,536,755 ÷ 427,849.06 ≈ 48个slice,DRAM为8,646,400 ÷ 720,533.33 ≈ 12个显存通道。slice是第二章介绍过的L2分区,各管一部分地址。再用"Max − Min"除以Average比较同类单元间的差距。SM为(441,248 − 435,302) ÷ 437,992.54 ≈ 1.36 %;L2约0.45 %,显存通道约0.35 %。这些结果说明同类单元的活跃时长比较接近,没有出现少数单元长期活跃、其它单元大部分时间空闲的明显失衡。
前面Instruction Statistics中,各调度器执行指令数的最大值与最小值之差约为平均值的6.0 %,比SM活跃周期的1.36 %差距更大。两者数的对象不同:SM只要还有warp驻留,活跃周期就会增加,warp等待期间也算;指令计数则只记录实际执行的指令。
本报告L1这一行的四个数都与SM相同,沿用各自的实测值即可。DRAM的平均活跃周期720,533.33大于SM的437,992.54,则与两级时钟频率和活跃比例都有关。报告DRAM Frequency为9.74 GHz,SM Frequency为1.39 GHz,约相差七倍;用流逝周期核对,37,220,352 ÷ 12 = 每显存通道3,101,696周期,再除以SM平均流逝442,680.22周期,得到约7.01。要比较各层级在自己的运行窗口里有多少周期活跃,就分别用同类单元的活跃周期总数除以流逝周期总数。DRAM为8,646,400 ÷ 37,220,352 ≈ 23.23 %,与第二章DRAM Throughput数值一致;L2为20,536,755 ÷ 20,860,896 ≈ 98.45 %,说明多数L2周期都有活动。L2的98.45 %量的是活跃周期占比,第二章Throughput的80.44 %量的是各通路相对自身处理峰值的利用程度。SM与SMSP平均活跃周期还相差约0.0117 %,两个层级分别按各自实测值计算。
GPU and Memory Workload Distribution补上了同类单元之间的比较:SM活跃周期最大值与最小值之差约为平均值的1.36 %,L2和显存通道的差距更小。结合各调度器的指令计数,本例在SM和调度器之间没有显示明显的整体负载失衡;SM持续有warp驻留但发射率低,主要等待仍要回到同步与相关指令队列分析。
这一章到这里就结束了。第二章讲三个重点section,本章补齐其余11个,Details页的14个section已经读完。PM Sampling展示运行过程,Compute Workload Analysis与Scheduler Statistics确认驻留充足但发射率低,Warp State Statistics把主要等待指向barrier和MIO队列。Instruction Statistics再把活动接回实际指令,互联与NUMA表交代硬件关系,Launch Statistics、Occupancy和Workload Distribution则分别核对资源配置、驻留上限和工作分配。
把这一章和第二章连起来,四个病态的证据就串起来了:非合并全局读让读取请求涉及的sector数量达到理想合并访问的8倍;共享内存冲突集中在两条LDS.128上,多出524,288个wavefront;树形归并每轮同步、越来越多线程因条件不满足而跳过加法,与Barrier停顿和12.45 %的有效线程比例相对应;最后8,192条STG则对应每个block只由一个线程写回结果。
这些证据共同说明,本例应优先减少等待,让已经驻留的warp更早具备发射条件。汇总指标给方向,逐行计数和采样把问题落回代码;规则的预计收益仍需经过实际修改和测量验证。下一章离开Details页,继续阅读其它页面。