实验一:显存解剖学与精度边界测试
1. 实验一:基础实验
注:
- 本实验所有机器均属于AutoDL 平台
www.autodl.com,执行前需自行注册账号并完成相关认证。- Happy Path:正常情况
- Sad Path:异常情况
1.1 第一阶段:实例租赁与环境准备
目标:获取一台 24GB 显存的机器(RTX 4090 为佳),并配置 PyTorch 环境。
-
租赁实例:
- 算力选型:在 AutoDL 算力市场,选择 RTX 4090 (24GB)。如果为了对比测试 OOM,也可以选 RTX 3090 (24GB) 或更小的卡(如 A4000 16GB,更容易触发 OOM)。
- 镜像选择:强烈建议选择基础镜像 Miniconda -> PyTorch -> 2.1.x 或 2.3.x -> Python 3.10 -> CUDA 11.8 或 12.x。
- 启动:开机后,点击“JupyterLab”进入控制台。
-
打开终端 (Terminal):
在 JupyterLab 页面底部点击“终端”图标。
-
安装依赖库:
你需要安装 transformers、accelerate(用于推理)、bitsandbytes(用于 Int4 量化)以及 modelscope(用于国内高速下载模型)。
hljs bash
1.2 第二阶段:模型下载(解决国内下载慢的问题)
目标:将 Qwen2.5-7B-Instruct 快速下载到本地硬盘(autodl-tmp 数据盘)。
-
创建下载脚本:
在终端输入以下命令创建一个下载脚本:
hljs bash -
编辑并运行下载代码:
双击左侧文件列表中的 download_model.py,粘贴以下代码:
hljs python -
执行下载:
hljs bash记录下终端输出的模型最终路径(例如 /root/autodl-tmp/qwen/Qwen2-5-7B-Instruct),下一步代码要用。
1.3 第三阶段:构建实验代码 (The Experiment)
目标:将逻辑转化为可执行的 Python 脚本,包含 Happy Path (BF16), Sad Path (FP32), 和 Int4 测试。
-
创建主实验脚本:
hljs bash -
编写代码(请将 model_path 替换为你上一步下载的实际路径):
hljs pythonhljs output
1.4 第四阶段:执行与观测
你需要同时看代码输出和系统监控。
-
开启系统级监控(分屏):在 JupyterLab 中,新建第二个终端窗口。输入以下命令,实时观察 GPU 显存变化:
hljs bash -
运行实验:回到第一个终端,运行 Python 脚本:
hljs bash -
实验结果示例:点击示例代码右上角的执行按钮,查看示例结果
1.5 第五阶段:实验结果分析指南
当脚本运行时,请重点观察以下数据,验证“理论背景”
- BF16 阶段 (Happy Path):
- Static VRAM: 观察 Model Loaded 时的 Allocated。7B 模型 BF16 应该在 14.5GB 左右。
- KV Cache: 观察 After Inference 相比 Model Loaded 增加了多少。增加的部分就是 KV Cache 和激活值。
- Reserved: 此时 Reserved 通常略大于 Allocated,这是健康的。
- Int4 阶段 (Quantization):
- Static VRAM: 应该大幅下降到 5GB - 6GB 左右。这是在消费级显卡部署的关键。
- FP32 阶段 (Sad Path):
- 脚本会尝试加载模型。
- 现象:你会看到加载过程变慢,nvidia-smi 中的显存占用迅速飙升接近 24GB (24576MiB)。
- 结果:Python 抛出 RuntimeError: CUDA out of memory。
- 专家点:仔细看脚本最后打印的 memory_summary,它会告诉你试图分配多少内存(比如 Tried to allocate 200MB),但剩余显存不足。
2. 实验一:进阶实验
基础实验主要关注静态权重和简单推理,忽略了几个导致生产环境崩溃的隐形杀手。
2.1 进阶 1:KV Cache 的线性爆炸 (Sequence Length Stress)
- 缺失点:基础实验只测了权重,没测长文本。现在的模型都支持 32k/128k 上下文,KV Cache 的显存占用公式是 2 * Layers * Hidden_Dim * Seq_Len * Batch_Size * Bytes。
- 操作:固定 Batch Size = 1,从 Seq_Len = 1k 开始,每次翻倍输入长度,直到 OOM。
- 观察:
- N 厂现象:随着长度增加,显存线性增长,直到 Flash Attention 优化介入。
- 国产挑战:我们的驱动在处理超长序列的大块连续显存分配时,Page Table(页表)的开销是否会导致额外的 Latency?
执行步骤
-
创建文件
exp1_kv_cache.py目标:测试 KV Cache 随序列长度变化的线性爆炸现象,直到触发 OOM。
hljs pythonhljs output -
运行文件
hljs bash -
观察结果:在页面中点击示例代码右上角的执行按钮,查看示例结果
2.2 进阶 2:显存碎片化与重分配 (Fragmentation & Reallocation)
- 基础实验缺失点:PyTorch 的 Caching Allocator 是把双刃剑。它不释放显存给 OS 是为了加速,但会导致碎片。
- 操作:
- 申请一系列不同大小的 Tensor(如 10MB, 20MB, 50MB...)。
- 随机释放掉中间的 Tensor(制造空洞)。
- 尝试申请一个大小等于“空洞总和”的 Tensor。
- 预期:物理显存够,但申请失败。因为 GPU 需要连续物理地址(或虚拟地址连续但物理页打散,取决于 MMU 实现)。
- 技术解剖:这是考验驱动显存管理单元(MMU)和 PyTorch 适配程度的时刻。MUSA 架构在这一点上投入了大量精力做页表合并。
执行步骤
-
创建文件
exp2_fragmentation.py目标:模拟显存碎片化。在总显存充足的情况下,制造“有空位但塞不进”的场景。
hljs pythonhljs output -
运行文件
hljs bash -
观察结果:在页面中点击示例代码右上角的执行按钮,查看示例结果
2.3 进阶 3:PCIe 带宽溢出 (Offloading Penalty)
- 操作:使用 accelerate 库,允许 device_map="auto" 将部分层卸载到 CPU RAM。
- 观察:当显存用完,模型开始吃内存时,推理速度(Tokens/s)的断崖式下跌。
- 锐评:这时候显存大小不是瓶颈,PCIe 带宽(通常是 PCIe 4.0/5.0 x16)成了那根细细的吸管。
执行步骤
-
创建文件
exp3_offloading.py目标:对比全 GPU 推理与 CPU Offload 推理的速度,验证 PCIe 带宽瓶颈。
hljs pythonhljs output -
运行文件
hljs bash -
观察结果:在页面中点击示例代码右上角的执行按钮,查看示例结果
3. 实验总结与核心知识点
3.1【核心结论】
显存不是一个简单的“水桶”,而是一个高度动态的“物流仓库”。权重是固定库存,KV Cache 是流动货物,而碎片化是导致仓库有空位却塞不进大件货物的元凶。
3.2【技术解剖:显存三态】
- Allocated (已分配):真正被 Tensor 数据占据的空间。
- Reserved (已预留):PyTorch 从驱动申请了但暂时闲置的空间。
- Fragmented (碎片):存在于 Reserved 中,但因地址不连续而无法使用的空间。
3.3【关键概念 (Knowledge Points)】
- OOM (Out Of Memory):分为“真 OOM”(物理显存真没了)和“假 OOM”(碎片化导致无法分配连续块)。
- KV Cache:Transformer 推理的显存杀手。FP16 下,每 Token 带来的显存增量是恒定的,上下文越长,它吃得越多,甚至超过模型权重本身。
- Quantization (量化):Int4 不仅仅是省显存,更是为了利用 Tensor Core 的整数计算单元(如果你硬件支持)来提升吞吐。
- Caching Allocator:PyTorch 的显存管理大管家。理解它,你才能理解为什么 nvidia-smi 显示占满了,但程序还能跑(只要 Cache 里有空位)。
在 2026 年,算力(FLOPS)往往是过剩的,显存带宽(Bandwidth)和容量(Capacity)才是大模型推理的真正货币。我们拼命做大显存位宽和容量,就是为了让用户在跑 70B 模型时,不至于因为少了 1GB 显存而被迫切成 4-bit 量化。