RTX 3090 的张量核心被解锁:一个内核模块赶在驱动绑定前,把 FP16/BF16 发射速率放开到满速
同一块卡、同一个 220W 功耗上限、同一份 PyTorch:RTX 3080 20GB 的 FP16 稠密 GEMM 从 58.0 TFLOPS 涨到 88.2(+52%),BF16 从 59.1 涨到 93.8(+59%)。
而同一批测试里,INT8 矩阵乘从 158 掉到 155 —— 那不是提升,是噪声。
只看到第一行,你会以为有人超频了;看到第二行才明白,这次改的不是算力,是发射速率。
被限住的到底是哪一段
专业卡和消费卡用同一颗 GPU 时,差距往往不在算力单元的数量上,而在「一条指令多久能被发射一次」。这次被放开的就是后者:累加器为 FP32 的 FP16/BF16 FMA 指令。
项目自己的描述很直白,Zagitta/gpu-unlock 的 README 抬头写的是 Unlock FP/BF16 instruction issue rate on Nvidia 3000’s cards to their professional equivalents,正文补了一句更精确的:会把 GA102、GA103、GA104、GA106 上「FP/BF16 进 FP32 运算」的指令发射速度翻倍。
- 影响的芯片:GA102 / GA103 / GA104 / GA106(这是项目自述,IT之家 10 月 25 日 07:09 的报道与之一致)
- 不受影响的是另一条链:INT8 走的是 IMLA 那套指令,所以它在所有报告里都是天然的对照组
- 它不动的东西也值得记住:显存带宽、CUDA 核心数量、可用的显存容量,一项都没变
判断一条「解锁」消息是不是营销,最快的办法就是找这种对照组。如果 INT8 和标量 FP32 也一起涨了,那测的就不是这个寄存器。
它是怎么做的:initramfs、两次寄存器写、一次 FLR
这套方案的思路是把 2018 年前后 CMP 挖矿卡解锁的老手艺(V67/PLM 那套定位方法)搬到消费级安培卡上,再换一个更早的时机执行:
- 编一个内核模块,放进 initramfs,让它排在
nvidia之前加载 - 此时卡还没被驱动绑定,模块写两个速度选择寄存器:SS0 写
0x88888888(选满 BF16 速率),SS1 写0x8(IMLA4,4-bit 寻址模式) - 触一次 FLR(功能级复位),把卡按冷启动路径重新初始化
- 驱动随后正常绑定,之后一切照旧
关键是它既不是 UEFI 应用、也不是改过的驱动二进制。所以驱动升级不需要重新打补丁 —— 这正是它相对前一代方案的卖点,也是这个仓库能在几天内涨到 3000 多 star 的原因。
安装流程就是标准的 DKMS + initramfs 套路:
git clone https://github.com/Zagitta/gpu-unlock
cd gpu-unlock
# 编译 DKMS 模块,并安装 initramfs / udev 集成
make install
# 默认是「关闭」,必须在内核命令行显式 opt-in
# Debian / Ubuntu (GRUB):编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX_DEFAULT 里追加
# gpu_unlock.targets=all
# 也可以只解指定的卡,地址用 lspci -D 打出来的格式
# gpu_unlock.targets=0000:01:00.0,0000:02:00.0
# Fedora 系用 grubby:
# grubby --update-kernel=ALL --args="gpu_unlock.targets=all"
# 改完必须重建 initramfs(mkinitcpio / update-initramfs / dracut 三选一,看发行版)
重启之后,用模块自己的日志核对,而不是靠猜:
# 模块每次解锁都会留日志
journalctl -k -b | grep gpu_unlock
# 看设备、驱动绑定、以及同一张卡上还有几个功能
lspci -nnk -s 01:00.0
# 想关掉:把 gpu_unlock.targets 从 cmdline 删除再重启,没有别的开关
几个 README 明确写出来、但很容易被忽略的约束:
- 它要求冷启动。开机之后再手动
modprobe没有意义 —— 驱动已经绑定了,寄存器写不进去。 - 而且在冷启动之前不要先去碰
nvidia-smi、CUDA、NVML 或nvidia-modprobe,一旦驱动抢了先,这轮就废了。 - 睡眠/待机后会掉:挂起会把卡断电,速度选择器被复位。模块会在 resume 之前重放一次解锁(约 1 秒,依赖
NVreg_UseKernelSuspendNotifiers=1),不想付这 1 秒可以gpu_unlock.resume=0关掉。休眠(hibernation)不在覆盖范围内。 - 固件状态在这一次开机里不会被重新上锁:模块会一直保留自己的 DMA 缓冲,直到断电。所以别强行卸载模块,也别指望热重试,想重来就断电重开。
数字要分三档读,混着读一定错
这次的消息里同时流通着三种来源的数据,它们的口径并不一样:
第一档:项目自述(含 issue #1 的 3090 Ti 表,450W)
| 负载 | 前 | 后 | 变化 |
|---|---|---|---|
| FP16 GEMM | 87.46 | 118.39 | +35% |
| BF16 GEMM | 87.50 | 157.41 | +80% |
| TF32 GEMM | 42.58 | 82.61 | +94% |
同一张卡、同一个功耗上限,FP16 只涨 35% 而 BF16 涨 80% —— 这说明放开的主要是 BF16 那条指令链,而不是「所有张量运算一起翻倍」。
README 里那张 tested cards 表更要按型号读:3090 区间 +25%~67%、3080 10GB +70-73%、3080 20GB +56-59%、3070 +73-81%、3060 12GB 约 +80%;GA103 的卡当时没有任何报告;3070 Ti 只在 Windows UEFI 那条支线上成功过;3080 Ti 20GB OEM 版解锁流程能跑完,但 BF16 仍然只有半速。
而且 README 自己点明了最大的混淆项:最低的那个 +25% 来自一张被限到 225W 的卡。功耗墙会严重稀释收益,脱离功耗上限谈百分比没有意义。
第二档:社区的受控 A/B(issue #15,10 月 23 日)
两台改过的 RTX 3080 20GB(设备 ID 10de:2206),Debian 13.6 + Proxmox VE 9.2.18、内核 7.0.14-16-pve、驱动 615.71.09,两臂都封在 220W,稠密 GEMM 8192²,5 次预热 + 每窗 20 次 torch.mm、取 5 窗中位数:
| 负载 | 卡 A 前→后 | 卡 B 前→后 |
|---|---|---|
| FP16 | 58.0 → 88.2(+52%) | 58.4 → 89.6(+54%) |
| BF16 | 59.1 → 93.8(+59%) | 59.0 → 92.2(+56%) |
| TF32 | 28.8 → 42.9(+49%) | 28.7 → 43.8(+53%) |
| INT8 | 158-160 → 155-160 | 同左,无变化 |
同一份报告里还有两个值得记住的结果:交替压 FP16/BF16 连续跑了 11566 次迭代,平均吞吐从 56 → 84-86 TFLOPS,峰值 66℃,平均功耗约 211W;以及解锁前后的输出按位一致(bit-identical)。
另一个 GA10x 的报告(issue #60,联想 OEM 的 3060 12GB)给出了更整齐的一组数:FP16 26.84 → 51.40(+91.5%)、BF16 26.82 → 53.13(+98.1%)、TF32 13.32 → 25.95,FlashAttention 类负载 +47%~68%。同一份报告里的对照组更能说明问题:FP32 标量 GEMM 9.31 → 9.40,INT8 _int_mm 21.89 → 22.01。
第三档:媒体转述(IT之家 2026-10-25 07:09,标题写「实际性能提升可达 60%」)
它引用的数字包括:3080 Ti 在 315W 下 BF16 71.59 → 113.98 TFLOPS、TF32 35.65 → 65.28;3080 10GB 在 320W 下 FP16/BF16 大约 65 → 110-113 TFLOPS;实验版 Windows 实现(UEFI 那条支线)在 3090 上 BF16 约 +82.5%。
同篇还引了 Reddit 上 ComfyUI 用户的读数:MOSS-Image SFT BF16 +58.6%、Qwen-Image 2.1 Turbo BF16 +57.2%、Z-Image Turbo BF16 +56.1%,而 MOSS-Image SFT 的 INT8 版本只有 +0.3% —— 又一次印证「只放开特定指令链」。
60% 这个数本身没错,但它是稠密 GEMM 的中位数区间,不是「所有负载都 +60%」。所以引用收益时至少要交代三件事:卡的型号与设备 ID、功耗上限、以及测的是寄存器发射速率还是引擎端到端。
先把报告复算一遍,再决定要不要动手
我写了个脚本把这些读数换算到同一口径(gain = after / before - 1),顺便核对「报告自称的区间」和实算是否一致。下面是它在本机的真实输出:
① 报告读数 -> 统一口径复算(gain = after/before - 1)
报告 / 负载 前 后 实算 自称 判定
--------------------------------------------------------------------------------------------
#15 3080-20G @220W FP16 GEMM 8192² 58.00 88.20 52.1% +52-54% 一致
#15 3080-20G @220W BF16 GEMM 59.10 93.80 58.7% +56-59% 一致
#15 3080-20G @220W TF32 GEMM 28.80 42.90 49.0% +49-53% 一致
#15 3080-20G @220W INT8 GEMM 158.00 155.00 -1.9% no change 一致
#25 3090 vLLM prefill (tok/s) 12346.00 16165.00 30.9% +33% 一致
#1 3090Ti @450W FP16 GEMM 87.46 118.39 35.4% — 自称留空
#1 3090Ti @450W BF16 GEMM 87.50 157.41 79.9% +68-80% 一致
#60 3060-12G FP16 GEMM 26.84 51.40 91.5% ~+80% 超出自称区间
#60 3060-12G BF16 GEMM 26.82 53.13 98.1% ~+80% 超出自称区间
#35 对照 fp32 标量 GEMM 9.31 9.40 1.0% control 一致
#35 对照 INT8 _int_mm 21.89 22.01 0.5% control 一致
两个具体结论:3060 那组实算是 +91.5% / +98.1%,比报告自称的「约 +80%」还高(README 的区间偏保守,引用时别把它当成天花板);3090 Ti 的 FP16 与 BF16 差得很远,任何「翻倍」的说法都要指明是 BF16 那条链。
为什么落到 LLM 推理上,加速会变得不均匀
最干净的一份端到端数据是 issue #25:一张没改过 VBIOS 的普通 3090,驱动 615.71.09、内核 7.0.0-38-generic,用 vLLM 0.30.0 服务一个 27B 的 BF16 模型,2904 token 提示 + 148 token 补全,取 3 次中位数:
| 指标 | 解锁前 | 解锁后 | 变化 |
|---|---|---|---|
| prefill 吞吐 | 12346 / 11857 tok/s | 16165 / 15992 tok/s | +33% |
| decode 吞吐 | 112.98 / 112.84 tok/s | 114.24 / 114.39 tok/s | +1.2%(等于没动) |
| TTFT | 0.235-0.248 s | 0.176-0.190 s | −24% |
issue #15 里还多一层对比:同样是 vLLM,prefill 在 8K/32K/128K 三个长度上都拿到 +33%~36%、TTFT −25%~26%;而换成 Strata(一个 GGUF 引擎)之后,8K 长度只有 +0.5%~0.8%(在噪声里),32K/128K 才涨 +12%~13%、TTFT −11%~12%。另一个四卡 3060 的报告里,Strata 的 prefill 从 450 到 600、decode 几乎没变。
规律不复杂:prefill 是算力受限的,decode 是显存带宽受限的。这类改动只放开指令发射,带宽一点没动,所以 decode 纹丝不动是预期结果,不是失败。而同一个引擎换内核实现后,能不能吃到这条指令路径,还取决于它实际调用了哪些 kernel —— 那才是「同样是 vLLM,一个 +36% 一个 +0.8%」的来源。
判断自己的负载能不能受益,按三步走最省事:
- 你的瓶颈在 prefill 还是 decode?decode 为主的任务(长对话、流式输出)大概率没收益。
- 你的引擎有没有走到 FP16/BF16 进 FP32 累加的那条路?(vLLM 系有明显收益,GGUF/部分自定义 kernel 收益小)
- 你的卡功耗上限多少?被限在 225W 以下的卡,收益会被功耗墙吃掉一大块。
至于游戏侧,目前没有可靠证据。README 自己不提带宽和解锁之外的能力,IT之家也把 DLSS 与游戏帧率归到「尚无可靠数据」那一类。
一段能在你自己机器上跑的基准
下面这份脚本要在有卡的机器上跑,本机没有 GPU,我只做了语法校验,没执行 —— 这一点我如实标出来。它跟社区报告的结构一致,并且强制带上 INT8 对照组:
# tensor_ab.py —— 解锁前后在同一张卡上跑同一份基准,两个方向都要测
import statistics
import torch
DEV = "cuda"
SHAPES = [(4096, 4096, 4096), (8192, 8192, 8192)]
def bench(dtype, iters=20, warmup=5, windows=5):
out = {}
for m, k, n in SHAPES:
a = torch.randn(m, k, device=DEV, dtype=dtype)
b = torch.randn(k, n, device=DEV, dtype=dtype)
for _ in range(warmup):
a @ b
torch.cuda.synchronize()
samples = []
for _ in range(windows):
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(iters):
c = a @ b
end.record()
torch.cuda.synchronize()
samples.append(start.elapsed_time(end) / iters)
ms = statistics.median(samples)
flops = 2 * m * k * n
out[f"{m}x{k}x{n}"] = round(flops / (ms * 1e-3) / 1e12, 1)
return out
def int8_control(iters=20):
"""INT8 是天然对照组:它要是也涨了,说明你测的根本不是这条指令链。"""
a = torch.randint(-8, 8, (8192, 8192), device=DEV, dtype=torch.int8).contiguous()
for _ in range(5):
torch._int_mm(a, a)
torch.cuda.synchronize()
start, end = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
start.record()
for _ in range(iters):
torch._int_mm(a, a)
end.record()
torch.cuda.synchronize()
ops = 2 * 8192 ** 3 / (start.elapsed_time(end) / iters * 1e-3)
return round(ops / 1e12, 1)
if __name__ == "__main__":
for name, dtype in [("fp16", torch.float16), ("bf16", torch.bfloat16)]:
print(name, bench(dtype))
torch.backends.cuda.matmul.allow_tf32 = True
print("tf32", bench(torch.float32)) # TF32 也要显式打开
print("int8 TOP/s", int8_control()) # 对照组:解锁前后应当基本不变
配套的三条判据,缺一条都不能说「成功了」:
- 寄存器读数:项目自己的 RM getter 会把 FMLA32/IMLA2/IMLA4 从半速读成满速 —— 这只是「设置生效」。
- 吞吐读数:上面这份脚本的前后对比 —— 这才是「有没有收益」。
- 数值一致性:同一批输入在解锁前后
torch.equal应当为真(社区 A/B 报告的结论是按位一致)。对上游任务来说,这一条比增益更重要。
Ada 那边:寄存器翻倍≠应用翻倍
10 月 23 日出现的分叉 JustAResearcher/gpu-unlock-ada 把同样思路搬到了 Ada 架构(4090 / 4070 Ti SUPER / 4060 Ti)。它的证据链比安培那边更完整,也更值得当反面教材看:
| 口径 | 前 | 后 | 变化 |
|---|---|---|---|
| 4090 寄存器发射速率 FP16 | 165.6 | 331.6 TF/s | +100% |
| 4090 寄存器发射速率 BF16 | 165.5 | 330.6 TF/s | +100% |
| 4070 Ti SUPER FP16(240W) | 95.0 | 189.9 TF/s | +100% |
| 4070 Ti SUPER INT8 | 379.5 | 379.5 TOP/s | 0 |
| E4M3 FP8 CUTLASS GEMM | 59.326 | 59.495 TF/s | +0.3% |
前两行写着 +100%,但同一次匹配跑的 cuBLAS GEMM 中位数只有:FP16 +12%~34%、BF16 两个 shape +16%~20%,4096³ 基本不变。寄存器翻倍,落到真实 kernel 上被压掉了大约三分之二。
这就是为什么我在开头先写 INT8 那个数字。发射速率是上限,不是收益;能拿到多少,取决于你的 kernel 有多少时间真的卡在那条指令的发射上。
顺带记两个细节:4060 Ti 16GB 的报告里,一次冷启动全过、驱动 595.99.02、getter 读回来也是满速,但没有同卡的 stock 对照 —— 所以那份报告只有「设置生效」,没有「吞吐收益」,引用时不能当性能数据用。另外 3070 Ti SUPER 在温热重启下也能成功,而 3090 那边 README 明确说不保证热重启。
还没被回答的问题
核对一圈下来,有几件事目前没有可靠数据,写在这里免得被当成已知结论:
- GA103 是零报告。README 的 tested cards 表里这颗芯片当时一条社区数据都没有。名单里有它,不代表有人在真机上验证过收益。
- Turing(20 系)不在支持范围内。README 自述的芯片只有 GA102/GA103/GA104/GA106,Ada 那条支线另算;仓库里确实有人开 issue 求 Turing 支持,但没有被实现。
- 笔记本端的支持是最近才补的:GA107 的 3050 6GB Laptop(设备 ID
10de:25ac)在 10 月 24 日才作为新设备被加进名单,也就是说这颗芯片上的收益数据几乎为零。 - 功耗墙的代价没有系统研究。只有零散线索:README 里最低的 +25% 来自一张被限到 225W 的卡。到底多少瓦以上才「不亏」,没人给出曲线。
- 长期稳定性只有两次像样的记录:一次是单卡 11566 次迭代的交替压测(峰值 66℃),一次是「一开机同时解锁多张卡」的成功记录 —— 安培那边有 4 卡与 7 卡同机解锁的报告,Ada 那边一次冷启动也能带过 4 张。几个月尺度的持续跑、以及解锁后的功耗与温度曲线,目前没有公开数据。
- 「解锁完但没收益」的样本真实存在(3080 Ti 20GB OEM、4060 Ti 16GB),说明能不能吃到这条路径,跟芯片、VBIOS、以及你用的 kernel 实现都有关 —— 这也是为什么我建议先复算报告、再上自己机器测。
风险清单:都能在 issue 里翻到
这类「改写厂商卡行为」的方案,风险跟收益一样具体:
- 解锁失败会把卡留下来当砖。issue #23(已关闭)记录某个 commit 在非冷卡上返回
-ETIMEDOUT,并且让这些卡之后无法再被 nvidia 驱动使用。 - 同一版本不是对每张卡都行。issue #20 里,同一台机器上的两张 3090 用旧 commit 解锁失败(等 GSP HALTED、WPR2 结束检查、SEC2 PRI 报错),换一个 commit 才过。
- 「脚本说成功」不等于「有收益」。issue #28 的 3080 Ti 20GB OEM:解锁完成、运行时读回选择器确实是满速,但 BF16 吞吐仍是半速。README 的 tested cards 表里就写着这一条。
- 还有卡直接报
firmware load failed (-2)(issue #67,GA104 的 3060 Ti LHR)。 - 打包集成的坑比内核本身还多:Debian 包没装 dracut drop-in 时,Ubuntu 25.10+ 永远拿不到 initramfs 集成(#54);Arch 包缺 dracut 早加载(#44);initramfs 构型里 HOOKS 含
systemd时会把模块名写成gpu_unlock?,结果模块永不加载(#29);如果 initramfs 里有nvidia.ko,它会抢在 gpu_unlock 之前绑定,解锁直接失败(#11)。 - 许可要自己看清楚:README 说内核模块与打包部分是 GPL-2.0-only,但我在 10 月 25 日实测 GitHub API 对该仓库的
license字段返回的是None(Ada 分叉才明确标 GPL-2.0)。要拿去做分发或商用,这个差异值得先问清楚,别只看 README。 - 最后一条与代码无关但更硬:在数据中心里用 GeForce 卡本身就有合规与保修层面的问题,本文只列风险面,不构成任何建议。
总结
这次解锁改的是指令发射速率这一个旋钮,而不是算力、显存或带宽。证据链也相当清晰:稠密 GEMM 与 prefill 涨 50%~60%(部分 GA10x 报告接近翻倍),INT8、标量 FP32 与显存带宽受限的 decode 一动不动,输出还能保持按位一致。
但引用数字前请把口径补齐:卡的型号与设备 ID、功耗上限、以及测的是寄存器还是端到端。同一个仓库的 README 里,最低的收益(+25%)和最高的收益(+98%)之间,差的不是技术,是那张卡当时被限在多少瓦。
至于要不要在自己的机器上装:它是内核模块 + initramfs 集成 + 冷启动要求,失败案例里有把卡留成砖的记录,而且集成坑几乎都在打包层而不是内核层。想试的话,用一台可以承受重装的机器,先跑一份带 INT8 对照组的基准,把解锁前的数字留档。
参考与致谢
- 项目仓库与 README:
https://github.com/Zagitta/gpu-unlock(本机 2026-10-25 抓取的main分支 README) - Ada 分叉:
https://github.com/JustAResearcher/gpu-unlock-ada - 社区受控报告:该仓库 issue #15、#25、#35、#55、#60
- 媒体转述:IT之家《解锁英伟达 RTX 30 系显卡张量核心:实际性能提升可达 60%》,2026-10-25 07:09(作者远洋)
- 未在本文使用的第三方说法:「所有游戏提升 xx%」类结论目前没有可靠来源,故未采纳。
RTX 3090 的张量核心被解锁:一个内核模块赶在驱动绑定前,把 FP16/BF16 发射速率放开到满速
- 作者
- 摆渡
- 发布于
- 许可协议
- CC BY-NC-SA 4.0
除另有声明外,本文采用上述许可协议。转载或改编请注明作者与原文地址,并遵守许可条款。