4. DMA传输性能模型:传输延迟公式、带宽计算、CPU负载卸载率评估
做嵌入式系统这么多年,我见过太多工程师把DMA当成一个「黑盒子」来用。配置好源地址、目的地址、长度,然后就指望它自动跑完。说实话,这样用DMA,性能能好才怪。
这一章,咱们把DMA的「底裤」扒开看看。传输延迟怎么算?带宽到底能跑多少?CPU到底被解放了多少?这些指标搞清楚了,你才能在设计阶段就预判性能瓶颈。
4.1 传输延迟公式:从请求到完成的时间账
DMA传输延迟,不是简单的「数据量÷总线频率」。我习惯把它拆成三部分:
- 启动延迟(Tstart):CPU写DMA控制寄存器、DMA仲裁获得总线控制权的时间
- 传输延迟(Txfer):数据在源和目的之间搬移的时间
- 结束延迟(Tdone):DMA完成中断响应、CPU读取状态的时间
总延迟公式:
Ttotal = Tstart + (N / Beff) + Tdone
其中 N 是传输字节数,Beff 是有效带宽
我在一个视频采集项目里遇到过这种情况:DMA配置了4KB的传输,结果每次启动延迟占了总时间的30%。为什么?因为DMA控制器每次都要重新仲裁总线,而CPU那边又在频繁访问DDR。后来我把传输块改成16KB,启动次数减少了,整体吞吐量直接翻倍。
我的经验:启动延迟是隐藏的杀手。对于小数据块(< 1KB),启动延迟可能比传输延迟还大。这时候用DMA反而不如CPU直接拷贝快。
4.2 带宽计算:理论值 vs 实际值
理论带宽很好算:总线频率 × 数据位宽。比如一个32位、100MHz的AHB总线,理论带宽是400MB/s。
但实际呢?我测过很多次,能跑到60%就算不错了。为什么?
- 总线仲裁开销:多个主设备抢总线,DMA得排队
- 内存控制器延迟:DDR的刷新、行激活、列选通,每一步都在吃时间
- DMA内部FIFO瓶颈:FIFO深度不够,总线会频繁停等
- 地址对齐问题:非对齐访问会触发额外的总线周期
| 场景 | 理论带宽 | 实测带宽 | 利用率 |
|---|---|---|---|
| 内存→内存(对齐) | 400 MB/s | 280 MB/s | 70% |
| 内存→内存(非对齐) | 400 MB/s | 160 MB/s | 40% |
| 外设→内存(单次触发) | 400 MB/s | 100 MB/s | 25% |
| 外设→内存(突发模式) | 400 MB/s | 220 MB/s | 55% |
你看,同样的总线,不同的传输模式,带宽利用率能差三倍。所以我说,别信数据手册上的理论值,拿示波器或者性能计数器实测才是王道。
4.3 CPU负载卸载率评估:到底省了多少?
这个指标最容易被忽悠。很多人觉得「用了DMA,CPU就完全解放了」。哪有那么好的事?
CPU负载卸载率,我习惯这么算:
卸载率公式:
U = (Tcpu_without_dma - Tcpu_with_dma) / Tcpu_without_dma × 100%
其中 Tcpu 是CPU处理数据传输所花费的时间
举个例子。一个UART接收任务,每秒接收1000个数据包,每个包256字节。
- 不用DMA:CPU每收到一个字节就要进一次中断,每次中断处理约5μs。每秒中断次数:256 × 1000 = 256,000次。CPU时间:256,000 × 5μs = 1.28秒。CPU占用率128%?对,已经超载了。
- 用DMA:DMA收完整个包才触发一次中断,每秒中断1000次。CPU时间:1000 × 5μs = 5ms。CPU占用率0.5%。
卸载率 = (1.28 - 0.005) / 1.28 ≈ 99.6%。这才是真正的解放。
注意:卸载率不是越高越好。DMA本身也会占用总线带宽,如果DMA把总线占满了,CPU访问内存反而变慢。我曾经在一个项目里把DMA优先级设得太高,结果CPU连中断向量表都取不出来——系统直接挂了。
4.4 综合性能模型:一张图看懂
下面这张图,是我做性能调优时常用的分析框架。它把DMA传输的各个环节串起来了。
这个模型告诉我们三件事:
- 启动和结束延迟是固定开销——传输块越大,分摊到每个字节的成本越低
- 有效带宽受限于最慢的环节——可能是总线、内存控制器,也可能是DMA的FIFO
- CPU卸载率不是100%——配置、中断、错误处理,CPU还是要干活
4.5 实战:用模型指导调优
我记得有一次调一个音频采集系统。I2S接口,48kHz采样率,16位立体声,每秒数据量才192KB。按理说DMA随便跑。但实际测下来,CPU占用率高达40%。
用我们的模型一分析:
- Tstart:每次传输前CPU要配置DMA,花了约8μs
- Txfer:实际传输192KB只需要约0.5ms
- Tdone:中断处理约10μs
问题出在哪?DMA配置的是每次传输64字节,每秒触发3000次中断。Tstart + Tdone 每次18μs,3000次就是54ms,CPU占用率5.4%。加上其他开销,就堆到了40%。
解决方案:把DMA缓冲区改成4KB,中断次数降到48次/秒。CPU占用率直接降到1%以下。
调优口诀:大块传输减少启动,突发模式提升带宽,对齐地址避免惩罚,优先级平衡别抢断。
嗯,这一章的内容就这些。记住,DMA性能模型不是纸上谈兵,它是你调优时的「导航地图」。下次遇到DMA性能问题,别急着改代码,先拿这个模型算一算,问题出在哪个环节,一目了然。