news 2026/4/27 2:11:22

GPU利用率分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
GPU利用率分析

GPU利用率详解

一、GPU利用率是什么?

GPU利用率(GPU-Util)是指GPU计算单元的使用率,反映了GPU的算力被利用了多少。

nvidia-smi 输出示例: +-----------------------------------------------------------------------------+ | GPU Name Utilization Memory-Usage | |=============================================================================| | 0 RTX 3080 95% 4500MB / 10240MB | +-----------------------------------------------------------------------------+ ↑ 这个就是GPU利用率

GPU内部结构(简化)

GPU = CUDA Cores(计算单元) + 显存(存储) + 缓存(Cache) ↑ ↑ ↑ 负责计算 负责存数据 加速数据访问

二、GPU利用率 vs 内存读取的关系

你的理解部分正确!让我详细解释:

场景1:计算密集型操作(高GPU利用率)✅

# 标准卷积:大量浮点运算 y = Conv2d(x) # GPU利用率: 90-95% # 执行过程: 1. 从显存读取数据 x (很快,~1ms) ↓ 2. CUDA Cores疯狂计算 (主要时间,~8ms) ↓ 3. 写回结果到显存 (很快,~1ms) 总时间: 10ms,其中8ms在计算 GPU利用率: 8/10 = 80%+ ✅

特点

  • 计算量 >> 内存访问量
  • CUDA Cores一直在忙
  • GPU利用率高

场景2:内存密集型操作(低GPU利用率)❌

# torch.roll:纯内存拷贝 y = torch.roll(x, shifts=1, dims=2) # GPU利用率: 20-30% # 执行过程: 1. 从显存读取数据 x (慢,~5ms) ↓ 2. 重新排列内存布局 (几乎没计算,~0.1ms) ↓ 3. 写回结果到显存 (慢,~5ms) 总时间: 10ms,其中0.1ms在计算 GPU利用率: 0.1/10 = 1% ❌

特点

  • 内存访问量 >> 计算量
  • CUDA Cores大部分时间在等待数据
  • GPU利用率极低

三、为什么内存读取会降低GPU利用率?

关键概念:计算/访存比(Compute to Memory Access Ratio)

计算/访存比 = 浮点运算次数 / 内存访问字节数
示例1:矩阵乘法(高计算/访存比)
# C = A @ B,其中 A, B 都是 1024×1024 A = torch.randn(1024, 1024) # 4MB B = torch.randn(1024, 1024) # 4MB C = A @ B # 计算量: FLOPs = 2 × 1024³ ≈ 2.15 GFLOPs # 内存访问: 读取A: 4MB 读取B: 4MB 写入C: 4MB 总计: 12MB # 计算/访存比: 2.15 GFLOPs / 12MB = 179 FLOPs/Byte ✅ 很高! # 结果:GPU利用率 90%+
示例2:torch.roll(低计算/访存比)
# 移位操作 x = torch.randn(8, 32, 224, 224) # 64MB y = torch.roll(x, shifts=1, dims=2) # 计算量: FLOPs = 0(没有浮点运算!) # 内存访问: 读取x: 64MB 写入y: 64MB 总计: 128MB # 计算/访存比: 0 FLOPs / 128MB = 0 FLOPs/Byte ❌ 太低! # 结果:GPU利用率 10-20%

四、GPU内存层级与性能

GPU内存层级(从快到慢)

寄存器 Register (最快) ↓ 共享内存 Shared Memory (~100x 比显存快) ↓ L2 缓存 Cache (~10x 比显存快) ↓ 显存 Global Memory (最慢,但容量大)

好的内存访问模式(高GPU利用率)

# 标准卷积:数据局部性好 for h in range(H): for w in range(W): # 访问 x[h-1:h+2, w-1:w+2] 的3×3邻域 # 相邻数据连续存储,L2 Cache命中率高 ✅ GPU利用率: 90%+

坏的内存访问模式(低GPU利用率)

# torch.roll:随机访问 for i in range(H): y[i] = x[(i+shift) % H] # 跳跃访问 # Cache命中率低,频繁访问慢速显存 ❌ GPU利用率: 20-30%

五、实际案例分析

UNet(高GPU利用率 90%)

时间线: ████████████ Conv1计算 (CUDA Cores忙碌) ████████████ Conv2计算 (CUDA Cores忙碌) ████████████ Conv3计算 (CUDA Cores忙碌) ... 特点: - 连续的大量计算操作 - 内存访问有规律,Cache命中率高 - CUDA Cores几乎一直在工作

Rolling-UNet(低GPU利用率 25%)

时间线: ██░░░░░░░░░░ roll1 (内存拷贝,CUDA Cores空闲) ██░░░░░░░░░░ roll2 (内存拷贝,CUDA Cores空闲) ██░░░░░░░░░░ roll3 (内存拷贝,CUDA Cores空闲) ████████████ Conv (CUDA Cores忙碌,但只占20%时间) ██░░░░░░░░░░ roll4 (又开始空闲...) ... 特点: - 大量时间在等待内存拷贝 - 真正计算的时间很少 - CUDA Cores大部分时间空闲

六、如何判断是计算瓶颈还是内存瓶颈?

方法1:看GPU利用率

nvidia-smi dmon -s u # 输出: # gpu pwr temp sm mem enc dec # 0 80 65 95 60 0 0 # ↑ ↑ # GPU利用率 显存带宽利用率 # 判断: sm高(>80%) → 计算瓶颈 ✅ 正常 sm低(<50%) → 可能是内存瓶颈 ❌

方法2:PyTorch Profiler

import torch.profiler as profiler with profiler.profile( activities=[profiler.ProfilerActivity.CUDA], with_stack=True ) as prof: output = model(images) print(prof.key_averages().table(sort_by="cuda_time_total")) # 输出示例: # Name Self CUDA time # of Calls # --------------------------------------------------- # aten::roll 25.3ms 60 ← 罪魁祸首! # aten::cudnn::convolution 5.2ms 10

方法3:计算理论利用率

# 假设 RTX 3080: 理论算力 = 29.77 TFLOPS (FP32) # 测量你的模型: 模型GFLOPs = 83.8G 实际延迟 = 8.86ms # 计算实际算力: 实际算力 = 83.8G / 0.00886s = 9.46 TFLOPS # GPU利用率: 利用率 = 9.46 / 29.77 = 31.8% # 判断: 如果模型都是标准卷积,31.8%已经不错(考虑其他操作开销) 如果模型用了很多roll/gather,说明被内存拖累了

七、总结

你的问题:"是不是有内存读取,利用率就比较低?"

答案:不完全是!关键看计算/访存比

操作类型计算量内存访问计算/访存比GPU利用率
标准卷积90%+ ✅
矩阵乘法90%+ ✅
BatchNorm50-70%
torch.roll极小极低10-30% ❌
Pooling30-50%

核心原则

好的设计:让CUDA Cores忙碌计算,而不是等待内存 坏的设计:CUDA Cores闲着,等待数据搬运

实用建议

  1. 多用标准操作(Conv, Linear)→ 高GPU利用率
  2. 少用内存操作(roll, gather, scatter)→ 避免低利用率
  3. 监控GPU利用率
    • >80%✅ 优秀
    • 50-80%⚠️ 还行
    • <50%❌ 有问题,检查是否有大量内存操作

一句话总结:不是"有内存读取就低",而是"计算太少、纯搬数据"才低!

核心发现:参数量和GFLOPs都不能单独决定推理速度

一、三个模型的性能对比

模型参数量GFLOPsFPS延迟GPU利用率
UNeXt-Stripe3.48M1.922G174.585.73ms~85%
UNet31.04M83.817G112.838.86ms~90%
Rolling-UNet1.78M3.219G31.1032.16ms~25%

关键矛盾

  • UNet的GFLOPs是Rolling-UNet的26倍,但反而快3.6倍
  • Rolling-UNet参数量最少,但推理最慢

二、决定推理速度的真正因素(重要性排序)

1.GPU利用率(最关键)

实际性能 = 理论GFLOPs × GPU利用率 × 硬件优化系数
  • UNet: GPU利用率90% → 虽然GFLOPs高,但几乎满载运行
  • Rolling-UNet: GPU利用率25% → 虽然GFLOPs低,但大量时间在等待

2.内存访问模式

  • 高效:连续内存访问(标准卷积)→ L2 Cache命中率>90%
  • 低效:随机内存访问(torch.roll)→ 大量cache miss

3.算子类型

  • 硬件友好:Conv2d, BatchNorm, ReLU → cuDNN极致优化
  • 硬件不友好:torch.roll, gather, scatter → 纯内存拷贝操作

4.GFLOPs(仅反映计算量,不包括内存操作)

5.参数量(只影响显存占用,几乎不影响速度)


三、为什么Rolling-UNet最慢?

问题1:torch.roll是内存密集型操作

# 每层需要6次移位操作 x1 = torch.roll(x[:, :c], shifts=-1, dims=2) # ~0.5ms x2 = torch.roll(x[:, c:2c], shifts=1, dims=2) # ~0.5ms # ... 共6次 # 10层 × 6次 × 0.5ms = 30ms(接近实测的32.16ms)

本质问题

  • torch.roll = 0 FLOPs(不计入GFLOPs统计)
  • 但需要重新排列整个特征图的内存布局
  • 触发GPU内存带宽瓶颈
  • 无法利用CUDA Cores/Tensor Cores

问题2:Kernel调度开销

UNet: Conv → BN → ReLU(融合后1次调用) Rolling-UNet: roll → roll → roll → roll → roll → roll → cat → Conv (10+次调用,每次~5-10μs启动延迟)

问题3:GFLOPs统计的误导性

GFLOPs只计算浮点运算,完全忽略了:

  • 内存拷贝操作(torch.roll, torch.cat)
  • 数据重排操作
  • Kernel启动开销

四、为什么UNet虽然GFLOPs高但仍然快?

优势1:cuDNN极致优化

# 标准卷积调用路径 nn.Conv2d(64, 128, 3×3) ↓ cudnnConvolutionForward() # NVIDIA官方优化 ↓ Tensor Core加速(FP16/TF32)

效率对比

  • cuDNN卷积效率:80-90%
  • RTX 3080理论算力:29.77 TFLOPS
  • UNet实际达到:9.46 TFLOPS(~32%利用率,考虑其他操作已经很好)

优势2:算子融合

# PyTorch自动融合 Conv → BN → ReLU ↓ 编译后 FusedConvBNReLU # 单次kernel调用

减少:

  • GPU kernel启动次数
  • 中间结果的内存读写

优势3:内存访问高度优化

输入 → im2col变换(硬件加速) → 矩阵乘法(Tensor Core) → 输出 所有操作都是coalesced memory access

五、为什么UNeXt-Stripe最快?

优势1:低GFLOPs架构设计

# 在高分辨率时使用少通道 E1: [B, 3, H, W] → [B, 16, H/2, W/2] # 16通道 E2: [B, 16, H/2, W/2] → [B, 32, H/4, W/4] # 32通道 # 在低分辨率时才增加通道 E4: [B, 160, H/16, W/16] # 分辨率已降低96%

优势2:标准卷积 + 高GPU利用率

  • 全部使用cuDNN优化的标准操作
  • GPU利用率达到85%

优势3:EdgeEnhancedStripeDecoder虽然复杂但高效

# 多分支并行计算,充分利用GPU并行性 main = self.main_conv(x) # 3×3 conv edge_x = self.edge_conv_x(x) # 3×3 conv (Sobel) edge_y = self.edge_conv_y(x) # 3×3 conv (Sobel) high_f = self.high_freq(x) # 1×1 conv # 这些可以在GPU上并行执行

六、性能分析方法

方法1:PyTorch Profiler

import torch.profiler as profiler with profiler.profile( activities=[profiler.ProfilerActivity.CUDA], record_shapes=True ) as prof: output = model(images) print(prof.key_averages().table(sort_by="cuda_time_total"))

方法2:GPU监控

nvidia-smi dmon # 实时监控GPU利用率

七、核心经验教训

❌ 不可靠的指标

  1. 参数量:只影响显存,不影响速度
  2. GFLOPs单独看:忽略了内存操作和硬件利用率

✅ 可靠的指标

  1. 实际延迟(ms):端到端性能
  2. GPU利用率:反映硬件使用效率
  3. GFLOPs + GPU利用率的组合

设计原则

  1. 优先使用硬件友好的操作

    • ✅ Conv2d, Linear, BatchNorm, ReLU
    • ❌ roll, gather, scatter, index_select
  2. 避免大量自定义内存操作

    • 用可变形卷积(DeformConv2d)替代显式移位
  3. 测量真实性能

    • 不要假设,用Profiler实测
    • 关注GPU利用率,不只是GFLOPs

八、优化建议

对于Rolling-UNet

# ❌ 慢:显式移位 x_shifted = torch.roll(x, shifts=-1, dims=2) # ✅ 快:可变形卷积 from torchvision.ops import DeformConv2d self.deform_conv = DeformConv2d(C, C, 3, padding=1)

通用原则

性能因素重要性排序

GPU利用率 > 内存访问模式 > GFLOPs > 参数量
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 23:44:45

把消息变成可运维资产:SAP Application Log 方法论与 BAL 全链路实战

在 ABAP 开发里,MESSAGE 当然好用:屏幕上立刻弹出报错,用户也能马上感知问题。但一旦场景从 对话框报错 走向 批处理作业、接口集成、异步队列、后台校验,单次弹窗就不够了——你需要的是一套能收集、持久化、检索、展示、归档的日志体系,让业务用户、运维同事、开发人员都…

作者头像 李华
网站建设 2026/4/18 20:09:35

小白逆袭!一文搞定Qwen3医学模型微调,DeepSeek式推理不再是专利!

Qwen3是阿里通义实验室最近开源的大语言模型&#xff0c;发布时便登顶了开源LLM榜单第一名。同时&#xff0c;Qwen系列模型也超越LLaMA&#xff0c;成为了开源模型社区中最受欢迎的开源LLM。 可以说&#xff0c;不论是进行研究学习&#xff0c;还是应用落地&#xff0c;Qwen已…

作者头像 李华
网站建设 2026/4/17 23:47:10

Miniconda-Python3.9环境下运行Stable Diffusion PyTorch代码

在 Miniconda-Python3.9 环境中高效运行 Stable Diffusion 的完整实践 你有没有遇到过这样的情况&#xff1a;从 GitHub 上克隆了一个热门的 Stable Diffusion 项目&#xff0c;满怀期待地执行 pip install -r requirements.txt&#xff0c;结果却卡在 PyTorch 安装环节&#x…

作者头像 李华
网站建设 2026/4/21 14:51:25

GitHub Sponsors支持开发者:Miniconda-Python3.9背后的技术推手

GitHub Sponsors支持开发者&#xff1a;Miniconda-Python3.9背后的技术推手 在人工智能实验室的某个深夜&#xff0c;一位研究生正准备复现一篇顶会论文。他克隆了代码仓库&#xff0c;运行 pip install -r requirements.txt&#xff0c;却在导入 PyTorch 时遭遇版本冲突——原…

作者头像 李华
网站建设 2026/4/20 0:07:23

PyTorch安装失败?试试这个Miniconda-Python3.9标准配置流程

PyTorch安装失败&#xff1f;试试这个Miniconda-Python3.9标准配置流程 在深度学习项目启动的前五分钟&#xff0c;你是否经历过这样的场景&#xff1a;满怀期待地运行 pip install torch&#xff0c;结果卡在依赖解析、编译失败或CUDA不兼容上&#xff0c;最终耗费数小时仍无法…

作者头像 李华