想象你接管了一家 GPU 大饭店的后厨,手下有两支神秘团队:cuBLAS 是刀工出神入化的切配组,cuDNN 是擅长大火快炒的掌勺组。你接了一单大生意——要在 NVIDIA 的灶台上搭建实时推荐系统。菜单上几十道“算子菜”:矩阵乘法、卷积、池化……但哪种做法最快?换一批客人(batch size)、换菜量(矩阵维度)、换食材等级(精度),速度排名又全变了。你需要一位带着秒表和记录本的品鉴师,把每道菜在不同条件下的出菜时间系统地测一遍,写成一册“点菜攻略”。这套用数据说话的方法,就是性能基准测试(Benchmark)——它不回答“这家店好不好吃”,只回答“这道菜在什么情况下上得最快、最划算”。

岗位关联

对 AI 从业者而言,掌握性能基准测试意味着你的模型优化不再靠感觉拍脑袋,而是有了一张可复用的量化底牌。无论调参、选型还是定位 GPU 瓶颈,它都是最硬核的决策依据。

目录
  1. 可视化演示
  2. 算子库与性能基准
  3. 核心算子原理
  4. 测试用例设计
  5. 基准测试流程
  6. 吞吐量分析
  7. 性能基线数据库
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

算子库与性能基准

品鉴师上任第一天要摸清家底。他把两位大厨叫到跟前:cuBLAS 刀工组,负责切切剁剁,一切矩阵乘法的活计都归它;cuDNN 掌勺组,负责煎炒烹炸,神经网络里最耗时的卷积操作是它的招牌菜。这两个算子库是 NVIDIA 官方精心调校过的“后厨班底”,普通人写矩阵乘法像用菜刀削土豆,它们却用上了特制的高速刀组——GPU 上的 SIMT 并行、寄存器和共享内存的层层调度,全被封装成了一个个开着现成接口的老火灶。

但“官方出品”不等于“永远最快”。同样一道矩阵乘法,换一批客人(batch size)、换一种食材分量(矩阵行列数),两位大厨的发挥天差地别。于是性能基准测试有了第一重意义:把抽象的“快”翻译成可比较的数字。它像品鉴师拿着秒表,记录每道菜从下单到上桌的真实时间,为后厨排班(算子选择)、食材采购(精度设置)提供不容辩驳的定量依据。

算法工程师做模型优化时,常常遇到两难:这个卷积该用 FP16 还是 FP32?batch size 开到 64 还是 128?没有基准数据,你就只能轮询试错、靠玄学调参。而一套规范化的 benchmark 能产出一张“性能地图”,让你在动手优化前就看清每条路的大致走向。

回看这几位后厨大厨:cuBLAS 和 cuDNN 再厉害,也需要品鉴师用吞吐量这把尺子量一量。尺子拿稳了,后面每道“菜”的评测才站得住脚。

Tthroughput=Nsamplesttotal\displaystyle T_{\text{throughput}} = \frac{N_{\text{samples}}}{t_{\text{total}}}
吞吐量的定义:每秒能处理多少个样本。分子是这批测试的样本总数,分母是算子实际消耗的总时间。
  1. 吞吐量符号$T_{\text{throughput}}$ 表示吞吐率,单位常写作 samples/s(每秒样本数),是把“快慢”变成数字的核心指标。
    Tthroughput=Nsamplesttotal\displaystyle T_{\text{throughput}} = \frac{N_{\text{samples}}}{t_{\text{total}}}
  2. 样本总数$N_{\text{samples}}$ 是所有测试样本的个数,相当于“一桌一共点了多少道菜”,样本越多,统计越稳。
    Nsamples\displaystyle N_{\text{samples}}
  3. 总耗时$t_{\text{total}}$ 是从开始计算到结束的墙钟时间,好比“最后一道菜上齐用了多久”,包含实际计算与调度开销。
    ttotal\displaystyle t_{\text{total}}

核心算子原理

品鉴师要写评测方案,先得看懂两道招牌菜是怎么做的。第一道是 GEMM——通用矩阵乘法(General Matrix Multiply)。想象你要把一批样本从输入层变换到隐藏层,每个样本的向量都要和权值矩阵做一次乘法累加。对 batch size 为 $b$ 的输入 $A$ 和权值 $B$,输出的第 $i$ 行第 $j$ 列,是 $A$ 的第 $i$ 行与 $B$ 的第 $j$ 列逐元素相乘后求和。这个过程的浮点运算量是固定的 $2mnk$:三个维度分别是输出行数、输出列数和累加长度。

第二道是卷积。它像一个滑动窗口在输入特征图上扫描,每滑到一个位置,就把窗口内的像素与卷积核逐元素相乘再求和,得到输出特征图的一个点。图像分类、目标检测这些任务几乎全靠它在底层撑腰。cuDNN 为卷积准备了多套算法:直接计算、隐式 GEMM、Winograd 变换……不同 shape 下各有胜负。

这套原理直接决定了 benchmark 设计的方向:既然耗时主要由 $m,n,k$ 和 batch size 决定,那么测试用例就要围绕这些维度展开;既然不同算法各有优劣,那么就要在统一接口下替算子库做“算法探试”。

理解了这两道菜,品鉴师才明白:原来出菜快慢,很大程度上已经被菜量($m,n,k$)和做菜方式(算法)写死在剧本里了。接下来要做的,就是设计一桌能覆盖各种口味的“评测套餐”。

FLOPsGEMM=2×m×n×k\displaystyle \text{FLOPs}_{\text{GEMM}} = 2 \times m \times n \times k
做一次 $m \times k$ 的矩阵乘 $k \times n$ 的矩阵,需要 $2mnk$ 次浮点运算。2 代表每次乘加算两次运算(一次乘法加一次加法)。
  1. 矩阵规模$m$ 是输出矩阵行数,$n$ 是输出矩阵列数,$k$ 是两个输入矩阵共享的累加维度。就像买菜:$m$ 是几道主菜,$n$ 是几样配菜,$k$ 是每种菜用几种调料。
    m,n,k\displaystyle m, n, k
  2. 乘法部分每个输出元素需要 $k$ 次乘法,总共 $m \times n \times k$ 次乘法,等价于“每道菜都翻炒 $k$ 勺调料”。
    m×n×k\displaystyle m \times n \times k
  3. 累加部分每次乘法后还有一次加法,所以总量翻倍,系数 2 就是这层含义。
    2×m×n×k\displaystyle 2 \times m \times n \times k

测试用例设计

设计套餐,最忌讳的是漫无目的地乱点菜。品鉴师遵循的第一原则叫控制变量:一次只动一个因素,其他全部锁死。他要摆弄的调料主要有三味。

第一味是 batch size——一次同时处理多少样本。GPU 是个流水线厨房,批量越大,每道菜的固定准备成本(内核启动、显存搬运)就被摊得越薄,吞吐量通常先涨后趋于饱和。第二味是矩阵维度 M/N/K——菜的分量。分量越大,计算密度越高,越能发挥 GPU 的算力。第三味是精度模式:FP32 像用精细电子秤称量每一位小数,FP16 像用托盘天平,速度快一半但可能称不准。此外还有 TF32、BF16 等变体。

把这三维组合起来,就形成了一个测试矩阵。用枚举法生成:batch size 取 {1, 4, 16, 64, 256},维度取 {128, 256, 512},精度取 {FP16, FP32},排列组合出几十到上百个用例。组合数量随维度增长呈乘积式爆炸,所以必须用手写脚本批量生成,而不是一个个手动敲。

三味调料齐了,套餐单子也拉好了。但光有点菜的单子还不够——后厨什么时候开火、每次烧几遍、记录本怎么填,这些流程细节,直接决定了评测数据可不可信。

Ncases=Nb×Ndim×Nprec\displaystyle N_{\text{cases}} = N_b \times N_{\text{dim}} \times N_{\text{prec}}
测试用例总数 = batch size 档位数 × 维度档位数 × 精度档位数。三者互相独立时,总数直接相乘。
  1. 档位数量$N_b$ 表示 batch size 取了几个档位(比如 5 个),$N_{\text{dim}}$ 是矩阵维度档位数,$N_{\text{prec}}$ 是精度档位数。
    Nb×Ndim\displaystyle N_b \times N_{\text{dim}}
  2. 组合计数把三个维度的档位数相乘得到总用例数。如同一家餐厅有 3 种汤、4 种主食、2 种甜点,套餐组合就是 $3 \times 4 \times 2$。
    Nb×Ndim×Nprec\displaystyle N_b \times N_{\text{dim}} \times N_{\text{prec}}
  3. 结果解读如果 batch 5 档、维度 3 档、精度 2 档,共 $5 \times 3 \times 2 = 30$ 个用例,每个用例都要跑多次取稳定值。
    Ncases=30\displaystyle N_{\text{cases}} = 30

基准测试流程

品鉴师把评测流程拆成四步走,每一步都像标准化厨房的 SOP(标准作业程序)。

第一步是制定测试计划。明确测试对象(是 cuBLAS 的 GEMM 还是 cuDNN 的卷积)、硬件环境(GPU 型号、驱动版本、算子库版本)和测量口径(以毫秒记还是微秒记)。环境不锁死,数据就是废纸。第二步是批量生成用例。用脚本把设计好的参数组合循环展开,每一条用例包含完整的输入 shape、精度和预热次数,自动生成可执行的测试代码。

第三步是采集性能数据。这里有个关键细节:正式计时前必须预热(warmup)。GPU 像刚启动的发动机,频率还没拉起来,缓存也是空的。先空跑几次让“水温上来了”,再开始计时。同样一个用例要重复多次,取中位数或最小值,而不是第一次的数值——因为环境抖动、临近任务的干扰都会让单次测量失真。第四步是结果记录与存储。每次实验的结果按统一格式写入 CSV 或数据库,附上环境信息,方便日后回溯比较。没有一个结构化的记录,测一百次也只是一堆散沙。

这套四步流程跑完后,品鉴师手上就有了一大摞记录。但“耗时 2.3 毫秒”这种孤零零的数字没有意义——它需要放在算力的天秤上称一称,才知道这道菜到底有没有发挥出厨房的全部潜力。

tˉ=1R∑i=1Rti\displaystyle \bar{t} = \frac{1}{R}\sum_{i=1}^{R} t_i
平均耗时 = 重复测 R 次的耗时之和除以 R。用多次测量平均,能有效抵消 GPU 频率波动造成的随机误差。
  1. 重复次数$R$ 是同一用例重复执行的次数,工程上常取 3~10 次。相当于一道菜让不同客人分别试吃,防止某位客人嘴馋吃快了。
    R\displaystyle R
  2. 单次耗时$t_i$ 是第 $i$ 次测量得到的算子耗时。每次测量的环境温度、频率状态都可能不同。
    ti\displaystyle t_i
  3. 求平均把所有单次耗时求和后除以 $R$,得到平均耗时的估计量 $\bar{t}$,作为该用例的代表值。
    tˉ=1R∑i=1Rti\displaystyle \bar{t} = \frac{1}{R}\sum_{i=1}^{R} t_i

吞吐量分析

拿到一个个耗时的原始记录,品鉴师要做的第二步是换算成吞吐量和利用率。先把 FLOPs 除以耗时得到实测 TFLOPS:计算量是固定的 $2mnk$,时间是测出来的,它们一比,就得到每秒完成了多少次浮点运算。接着把实测 TFLOPS 除以 GPU 的理论峰值 TFLOPS,得到算力利用率。利用率 80% 说明后厨火力全开;利用率只有 20%,就要警惕了:是不是菜量太小(小矩阵),大部分时间浪费在“端菜”(数据传输和内核启动)上?

这就是著名的瓶颈定位逻辑。当矩阵很小时,GPU 计算核心大部分时间在等待数据从显存搬进来,此时是带宽瓶颈;当矩阵足够大时,计算核心满负荷运转,此时才逼近计算瓶颈。品鉴师要把每个用例的利用率标注在图表上,一眼看出哪道菜是“配菜拖累了主厨”。

分析做完,品鉴师手里的零散记录终于拼出了一张有洞察的性能地图。但单份地图只对一次评测有效——他把所有地图装订成册,就建起了后厨最宝贵的资产:性能基线数据库。

Utilization=TFLOPSmeasuredTFLOPSpeak×100%\displaystyle \text{Utilization} = \frac{\text{TFLOPS}_{\text{measured}}}{\text{TFLOPS}_{\text{peak}}} \times 100\%
算力利用率 = 实测每秒浮点运算次数 / GPU 理论峰值每秒浮点运算次数。它回答“厨房火力开了几成”。
  1. 实测算力$\text{TFLOPS}_{\text{measured}}$ 是用 $\text{FLOPs} / t_{\text{total}}$ 换算出的每秒浮点运算量,单位是“每秒万亿次浮点运算”。
    TFLOPSmeasured=2mnkt\displaystyle \text{TFLOPS}_{\text{measured}} = \frac{2mnk}{t}
  2. 理论峰值$\text{TFLOPS}_{\text{peak}}$ 是 GPU 厂商标称的最大算力,如 A100 的 FP16 峰值 312 TFLOPS。这是“满功率运转”的上限。
    TFLOPSpeak\displaystyle \text{TFLOPS}_{\text{peak}}
  3. 比值与百分比实测除以峰值,再乘 100% 得到利用率。利用率接近 100% 说明瓶颈在计算,利用率低说明瓶颈在搬运(带宽)。
    Utilization=TFLOPSmeasuredTFLOPSpeak×100%\displaystyle \text{Utilization} = \frac{\text{TFLOPS}_{\text{measured}}}{\text{TFLOPS}_{\text{peak}}} \times 100\%

性能基线数据库

最后一步,是把所有评测结果沉淀成一本可以反复查阅的“点菜攻略”。这本攻略的第一项功能是横向对比:同样一个 GEMM,batch 32 和 batch 128 谁快?FP16 比 FP32 快多少?一目了然。第二项功能是版本回归:NVIDIA 更新了 cuDNN 版本,你担心升级后某些 shape 反而变慢。怎么办?把新旧版本在相同用例集上各跑一遍,拿新结果去和基线库里的旧记录比,慢于基线 5% 就报警。第三项功能是最优配置推荐:模型训练时该不该把 torch.backends.cudnn.benchmark 打开?打开后算子库会在第一次调用时遍历所有算法,选最快的那个,后续复用。而这个选型决策的依据,正是基线数据库中积累的 shape→最优算法映射。

更好的做法是把这套基准做成自动化回归:每次代码提交或环境变更,CI 流水线自动跑一遍核心用例集,把关键指标画成趋势图。某天曲线突然下跌,说明最新的改动引入了性能回退,开发者能第一时间定位。这就把一次性的评测,升级为持续保障性能的引擎。对模型优化来说,基线数据库是“参考书”——面对新的网络结构,先查库,再决定精度、batch size 与算子算法,避免从零试错。

从最初的秒表计时,到如今的自动化回归与智能选型,性能基准就这样完成了从“一次评测”到“持续资产”的跃迁。品鉴师的这本攻略,让后厨的每一次决策都有了据可查、有数可依。

Speedup=tbeforetafter\displaystyle \text{Speedup} = \frac{t_{\text{before}}}{t_{\text{after}}}
加速比 = 优化前耗时 / 优化后耗时。大于 1 说明优化有效,等于 1 说明没有变化,小于 1 说明反而变慢了。
  1. 基线耗时$t_{\text{before}}$ 是优化前(或旧版本)的算子耗时,存储在基线库中作为参照物,相当于“上一次的品鉴记录”。
    tbefore\displaystyle t_{\text{before}}
  2. 当前耗时$t_{\text{after}}$ 是优化后(或新版本)的算子耗时,通过新一轮 benchmark 测得。
    tafter\displaystyle t_{\text{after}}
  3. 比值判断两数相除得到加速比。Speedup = 1.2 表示快了 20%,Speedup = 0.9 表示慢了约 11%,是版本回归的直接证据。
    Speedup=tbeforetafter\displaystyle \text{Speedup} = \frac{t_{\text{before}}}{t_{\text{after}}}

AI 应用场景

训练框架的算子选型

阶段:模型训练与部署优化

解决的问题:深度学习框架调用 cuDNN 时,默认不进行算法探试,可能选了次优算法。而 torch.backends.cudnn.benchmark = True 会在首次调用时通过基准测试选出最快的卷积算法。传统做法是凭经验开关,不知道打开后是否真的更快;用性能基线数据可以判断当前网络 shape 下探试收益有多大。

具体案例:某推荐系统团队训练一个 3D 卷积模型,发现训练速度忽快忽慢。工程师设计了一组覆盖输入 shape 的 GEMM/卷积 benchmark,对比开启 benchmark 模式前后的耗时,发现 3D 卷积在开启探试后提速 32%,同时确认了小 batch 场景下探试开销反而大于收益,于是按 batch 大小动态开关。

推理服务的动态批处理配置

阶段:模型推理与 serving

解决的问题:在线推理系统面对波动的请求量,需要决定 batch size 设多大才能最大化吞吐。传统做法是固定 batch=32,造成闲时浪费、忙时排队。基于性能基线数据库,可以画出一条“吞吐量 vs batch size”曲线,找到拐点,让服务在拐点附近弹性伸缩。

具体案例:一家 OCR 服务商对 cuBLAS GEMM 做了 batch = 1/4/16/64/128 的基准扫描,发现 batch=64 时 TFLOPS 已趋饱和,再增大收益极小且显存压力变大。于是把动态批处理的上限设为 64,并配合预热池,最终在同等 GPU 数量下将 QPS 提升了 2.1 倍。

算子库升级的 CI 回归门禁

阶段:平台工程与持续集成

解决的问题:NVIDIA 发新版 cuDNN/cuBLAS 后,直接升级有性能回退风险。传统做法是升级后人工抽查,粗糙且不可追溯。将固定用例集的 benchmark 接入 CI,任何算子库/驱动版本变更都自动对比历史基线。

具体案例:某自动驾驶公司维护一个 500 行用例的算子基准集,覆盖训练与推理的关键 shape。某次 cuDNN 升级后 CI 报警:特定 5×5 卷积在 FP16 下慢 14%。团队定位到是新版回退到非 Winograd 算法,于是锁定版本并在基线库中标记该 shape 的可用版本范围。

Python 演示

python52 行
import timeimport csvimport numpy as np  def run_gemm_like(a_shape, b_shape, dtype=np.float32):    """模拟一次 cuBLAS GEMM 调用, 返回耗时秒数与输出形状。"""    a = np.random.rand(*a_shape).astype(dtype)    b = np.random.rand(*b_shape).astype(dtype)    t0 = time.perf_counter()    np.dot(a, b)    elapsed = time.perf_counter() - t0    return elapsed  def measure_once(batch, m, k, dtype, repeat=3):    """测试一个用例, 重复多次取最小耗时, 换算成 TFLOPS。"""    n = k    times = []    for _ in range(repeat):        a_shape = (batch, m, k)        b_shape = (k, n)        times.append(run_gemm_like(a_shape, b_shape, dtype))    best = min(times)    flops = 2.0 * batch * m * n * k    return best, flops / best / 1e12  def main():    dtype_map = {"fp32": np.float32, "fp16": np.float16}    configs = [        (1, 128, 128, "fp32"),        (4, 128, 128, "fp32"),        (16, 128, 128, "fp32"),        (64, 128, 128, "fp32"),        (64, 256, 256, "fp32"),        (64, 512, 512, "fp16"),    ]    rows = []    for batch, m, k, name in configs:        sec, tflops = measure_once(batch, m, k, dtype_map[name])        rows.append([batch, m, k, name, round(sec, 5), round(tflops, 2)])        print("%4d x %4dx%-4d %-5s 耗时 %7.5fs  吞吐 %7.2f TFLOPS" % (batch, m, k, name, sec, tflops))    with open("gemm_bench.csv", "w", newline="") as f:        writer = csv.writer(f)        writer.writerow(["batch", "m", "k", "dtype", "time_s", "tflops"])        writer.writerows(rows)    print("结果已写入 gemm_bench.csv")  if __name__ == "__main__":    main()
目的:演示一个标准 GEMM 基准测试脚本的结构——遍历参数组合、重复测量取最优耗时、换算 TFLOPS、结果写入 CSV。真实场景中只需把 run_gemm_like 替换为 cublasSgemm 或 cudnnConvolutionForward 即可。
  1. configs = [...]定义测试用例集合,包含 batch size、M/K 维度与精度档位,对应正文“测试用例设计”中的组合矩阵。
  2. times.append(run_gemm_like(a_shape, b_shape, dtype))对同一用例重复执行,把多次耗时收进列表,供后续取最小稳定值。
  3. best = min(times)取最小耗时而不是平均耗时,用来模拟“热身后最优状态”,对应 warmup 后取最优的行业惯例。
  4. flops = 2.0 * batch * m * n * k用正文公式 FLOPs = 2mnk 计算该用例的浮点运算总量。
  5. round(tflops, 2)把 TFLOPS 保留两位小数,便于阅读与归档。
  6. writer.writerow([...])将每条用例的结果串行写入 CSV,对应“结果记录存储”环节,为性能基线数据库提供原始素材。

常见误区

误区cuDNN 是 NVIDIA 官方库,里面每个算子都经过了深度优化,我直接调它就行了,不需要自己测什么基准。

正解cuDNN 内部为同一个卷积准备了多套算法,如直接卷积、隐式 GEMM、Winograd,它们在 batch size、kernel size、精度不同时表现各异。官方库也不保证每个 shape 都选了最优解。正常情况下需要开启 cudnn.benchmark 模式做算法探试,或者主动用 benchmark 脚本对目标 shape 做网格扫描,才能确定最快算法。

为何常见:初学者容易把“官方优化”和“自适应最优”混为一谈。事实上官方库更像一座装备齐全的厨房,菜刀虽好,也要选对型号才顺手。

误区FP16 数据量是 FP32 的一半,带宽减半,又有张量核心加速,所以同样模型直接换成 FP16,速度必然翻倍。

正解FP16 的收益取决于瓶颈类型。当算子处于计算瓶颈时,张量核心确实能带来接近翻倍的吞吐;但当矩阵很小时,耗时集中在数据搬运和内核启动上,FP16 的优势被带宽瓶颈掩盖,实测可能只快 10% 甚至无提升。另外 FP16 动态范围小,直接使用可能造成数值溢出,需要配合 loss scaling。

为何常见:很多人只看到“半精度=一半数据=一半时间”的表面逻辑,忽略了计算、带宽、启动三类开销的复杂博弈。

误区我今天在 GPU 上测出 GEMM 耗时 2.1 毫秒,明天还应该是 2.1 毫秒,这个数据存下来可以直接用。

正解GPU 的时钟频率会随负载和温度动态变化,相邻进程、显存分配状态也会影响结果。单次测量可能偏离真实值 10% 以上。正确做法是每个用例预热后重复多次,取最小值或中位数,并在记录中附上驱动版本、算子库版本、GPU 型号等环境信息,否则数据无法跨时间对比。

为何常见:人们习惯把计算机的运行视为确定性的,忽略了现代 GPU 的功耗管理与多租户干扰等现实因素。

误区A100 FP16 峰值 312 TFLOPS,我的 benchmark 显示 260 TFLOPS,说明性能已经很好了,不用再优化。

正解峰值是理想状态下的理论上限,真实性能要看算力利用率。小矩阵场景下利用率可能只有 15%,此时瓶颈不在算力而在带宽。应该同时测量耗时的绝对值和利用率,并对比同 shape 下不同算法的表现,才能判断“是计算满了还是等数据等崩了”。

为何常见:厂商宣传口径中峰值最抓眼球,而实际开发中 shape、精度、访存模式才是决定性能的隐藏变量。

面试题

Q1. cuBLAS 算子库最擅长加速以下哪类运算?

单选题 · easy

  • A.卷积神经网络中的卷积
  • B.通用矩阵乘法 GEMM← 正确答案
  • C.图像缩放与裁剪
  • D.文本分词与编码

答案:B。cuBLAS 是 NVIDIA 的 BLAS(基础线性代数)库,核心是矩阵与向量运算,其中 GEMM 是最常用、优化最深的算子。卷积主要由 cuDNN 负责,图像与文本处理则不在它的职责范围内。

Q2. cuDNN 算子库的核心作用是?

单选题 · easy

  • A.加速卷积等深度学习算子← 正确答案
  • B.管理 GPU 显存分配
  • C.编译 Python 源代码
  • D.提供 Web 服务框架

答案:A。cuDNN 全称 CUDA Deep Neural Network library,专门为深度网络中的卷积、池化、归一化等算子提供高性能实现。显存管理由 CUDA runtime 负责,编译与 Web 服务与它无关。

Q3. 性能基准测试最主要的目标是?

单选题 · easy

  • A.建立量化的性能基线← 正确答案
  • B.增加 GPU 的显存容量
  • C.加速磁盘的读写速度
  • D.生成随机的测试种子

答案:A。基准测试是通过受控实验测量算子的吞吐量与耗时,形成可对比的基线数据库。它不能改变硬件参数,而是为算子选型和优化提供定量决策依据。

Q4. 一个 GEMM 的 m=n=k=1024,其浮点运算量约为多少?

单选题 · mid

  • A.约 1G FLOPs
  • B.约 2G FLOPs← 正确答案
  • C.约 4G FLOPs
  • D.约 512M FLOPs

答案:B。FLOPs = 2 × 1024 × 1024 × 1024 = 2,147,483,648 ≈ 2.1 GFLOPs。2 代表乘法和加法各算一次。留意别漏掉系数 2。

Q5. 做算子性能测试时为什么要先 warmup(预热)?

单选题 · mid

  • A.让 GPU 频率拉升且缓存就绪← 正确答案
  • B.减少代码编译的时间
  • C.增大 GPU 显存空间
  • D.防止数据被意外删除

答案:A。预热让 GPU 进入稳定工作状态,避免首轮调用时驱动加载、缓存未命中和频率爬坡造成的虚假慢速。没有预热的数据更像“冷启动成绩”,不能代表真实运行性能。

Q6. FP16 相比 FP32 更快的核心原因是?

单选题 · mid

  • A.数据量减半且张量核心加速← 正确答案
  • B.数学上精度更高
  • C.不需要占用显存
  • D.完全不需要做乘法

答案:A。FP16 每个数占 2 字节,传输带宽与存储占用减半;NVIDIA 张量核心对 FP16 算力是 FP32 的数倍。精度更高是 FP32 的特点,FP16 仍需运算与存储。

Q7. 某 GPU 峰值 100 TFLOPS,实测利用率 30%,则实测算力为?

单选题 · hard

  • A.30 TFLOPS← 正确答案
  • B.300 TFLOPS
  • C.333 TFLOPS
  • D.70 TFLOPS

答案:A。实测算力 = 峰值 × 利用率 = 100 × 0.3 = 30 TFLOPS。利用率 30% 意味着算力严重闲置,需要进一步排查是否处于带宽瓶颈或小矩阵场景。

Q8. cuDNN benchmark 模式做算法探试时,通常需要考虑哪些因素?

多选题 · mid

  • A.输入 tensor 的 shape← 正确答案
  • B.数据类型与精度模式← 正确答案
  • C.候选算法集合← 正确答案
  • D.服务器摆放的位置

答案:ABC。算法探试是在给定 shape 与精度下,遍历所有候选算法实测耗时并选择最优。服务器位置是物理部署因素,与算法选择无关。

Q9. 性能基线数据库可以支撑哪些应用?

多选题 · hard

  • A.检测算子库版本性能回退← 正确答案
  • B.为模型算子选择最优配置← 正确答案
  • C.指导精度模式的切换← 正确答案
  • D.直接增加 GPU 的流处理器

答案:ABC。基线库存的是“哪个配置最快”的历史数据,可做版本 A/B 对比、配置推荐和精度策略参考。它无法改变硬件资源,流处理器数量是硬件的固定属性。

Q10. 算子库升级后做 A/B 回归对比时,需要控制哪些变量以保证结论可靠?

多选题 · extreme

  • A.输入 shape 与精度保持一致← 正确答案
  • B.使用相同的测量统计口径← 正确答案
  • C.固定 GPU 驱动版本与机器状态← 正确答案
  • D.故意使用不同的 batch size

答案:ABC。控制变量是基准测试的基石。shape、精度、统计口径与运行环境必须一致,才能把性能差异归因于算子库版本。故意改 batch size 会让结果无法归因,属于无效实验。