资讯详情

共享buffer后DDR流量不降?从ISP到NPU数据链路的排查与优化

📅 2026/10/12 1:12:04 | 华诺云谱 👁 阅读
共享buffer后DDR流量不降?从ISP到NPU数据链路的排查与优化
我调一块带 ISP 和 NPU 的视觉 SoC 时最先听到的建议就是“把 Camera 到 NPU 的 buffer 共享掉DDR 流量能省一半”。当时整个优化组都觉得这是最稳的一招ISP 写完图像NPU 直接读同一块内存中间不再复制一份DDR 读写量从四次变两次怎么会不降可等我真把驱动改成共享 dma-buf、跑完一版性能测试看板子上 DDR 总带宽计数数字几乎纹丝不动。那几天我一直盯着总线监控页面怀疑人生后来把计数器拆开一层层查才搞明白“共享 buffer”和“DDR 流量下降”之间隔着一堆大家默认不说破的前提。这篇文章就把我当时踩过的坑、查过的链路、最后真正见效的手段完整写出来。1. 先说清楚共享 buffer 到底应该省掉哪段流量1.1 两次搬运和四次搬运的差别要判断一个优化有没有生效先得知道预期收益是哪里来的。图像从 Camera 到 NPU通常要经过两个“角色”ISP 把 sensor 的 Bayer/RAW 数据变成 YUV 或者 RGB 图像NPU 再把图像吞进去做模型推理。传统的驱动实现里这两个角色各管各的内存ISP 输出到物理地址 A 的 buffer驱动申请一块新的 NPU 输入 buffer B软件把 A 的内容拷贝到 BNPU 的 DMA 再把 B 读进自己的内部 SRAM。这样一帧图像在 DDR 侧的访问次数是ISP 写 A 一次拷贝过程读 A 一次、写 B 一次NPU 读 B 一次。加起来四次 DDR 操作。共享 buffer 的处理就是让 NPU 直接去读 AISP 写 A 之后不需要再去写 B。于是四次变两次ISP 写一次NPU 读一次。理论省一半这个账小学生都能算明白。1.2 一笔账算下来省一半并不夸张我们按常见的 1080P NV12 格式估算一下。NV12 每像素 12bit1920×1080 一帧大约 3.1MB。如果帧率 30fps那么 ISP 输出速率大约是 93MB/s。传统模式里每帧的 DDR 流量是ISP 写 A3.1MB拷贝 A 到 B读 3.1MB 写 3.1MBNPU 读 B3.1MB一帧合计 12.4MB换算成每秒就是约 373MB/s。共享 buffer 之后只剩 ISP 写 A 的 3.1MB 和 NPU 读 A 的 3.1MB一帧 6.2MB每秒约 186MB/s。省下来的大约 187MB/s 听起来不少但对一颗 DDR 总带宽动辄 2~3GB/s 的 SoC 来说占比也就 6~10%。如果你观测点放错了或者系统里还有其他业务在跑这点变化很容易被噪声吞掉。我最初就是在全局带宽页面上看数字翻来覆去看了好几遍都以为驱动没生效。1.3 为什么你测的数字没有按这个账本走这套账目成立有两个隐藏前提。第一系统里确实存在一次 A 到 B 的拷贝。如果你的内核和驱动早就用了 v4l2 的 dma-buf 传递原来就没有那份额外拷贝那么改成“共享 buffer”只是把内存占用从两份变成一份DDR 流量自然一点都不会降。第二ISP 输出格式和 NPU 输入格式完全一致。只要中间有任何格式转换、缩放、padding驱动就有充分的理由申请一个中间 buffer把省掉的流量又送回去。这两个前提几乎覆盖了我在实际调试中遇到的绝大多数“共享了但没降”的场景。2. 动手排查先验证“共享”两个字是真的还是驱动在演2.1 第一件事确认物理地址只有一份拿到问题先别急着折腾带宽计数器先确认共享到底是不是真共享。Linux 下通过 dma-buf 的 debugfs 可以直接看到每个 buffer 的物理地址和引用关系cat /sys/kernel/debug/dma_buf/bufinfo重点看两块ISP 写的那块输出的地址和 NPU 驱动拿到 input 的地址物理地址是不是同一个。我那次排查就发现NPU 驱动内部为了统一走自己的内存分配器又调用 ION/CMA 申请了一块内存把内容再倒腾了一遍。表面上是共享 fd实际上只有句柄共享物理页完全是两套。这种情况后面所有关于带宽的分析都没有意义先把驱动逻辑理顺再继续。2.2 第二件事打开驱动里的 trace看有没有隐藏的 memcpy驱动代码里藏得很深的一种做法是收到 dma-buf 后先不给 NPU而是用dma_buf_map映射到 CPU 地址空间然后在格式转换循环里逐行拷贝到另一块“更适合 DMA”的连续内存里。这种代码表面上看是共享 buffer实际每帧都在做一次不折不扣的全尺寸搬运。可以用trace-cmd或者内核的mmap/memcpy追踪点配合perf trace去看 CPU 访问 DMA buffer 的时间段和访问量。如果同一块 buffer 在投递给 NPU 之前出现了一大段读 写的地址序列那基本就是驱动在做隐藏拷贝。很多情况下驱动注释里写着“为了提升 DMA burst 效率”但它把共享 buffer 省的流量全部吃回去了。2.3 第三件事用 DMA descriptor 核对源目的地址如果怀疑到 DMA 层面就不要只看代码直接看硬件描述符。大多数 DMA 控制器都有寄存器回读接口或者可以通过调试日志把每次 DMA 的源地址、目的地址、长度打出来。我当时的做法是在 NPU 固件里加了一条日志把每次搬运的 source 和 dest 地址 dump 出来跑两轮就发现它从 buffer A 到 buffer C 又额外搬了一次。这个方法的好处是绕过了驱动的抽象层。驱动代码可能很绕但 DMA descriptor 是硬件最终执行的真相。看到 source 和 dest 指向不同物理页业务层再怎么说“共享”都没用流量就是实实在在多了一倍。3. 扒开“共享”的皮流量藏在四个地方3.1 NPU 必须要读的那一次怎么都省不掉共享 buffer 省掉的只是“额外拷贝”但 NPU 要把 input 从 DDR 吃到内部 SRAM这一步 read 是无论如何拿不掉的。如果你的系统原本就是 ISP 写 A、NPU 直接读 A 的设计那么共享前后 DDR 读流量本来就没变化。更需要注意的一点是 NPU 内部架构。一类 NPU 有较大的内部 SRAMDMA 会一次把输入连续若干行读进去另一类是流式架构边读边算。前者如果 SRAM 容量放不下整张输入图DMA 同一个区域可能被读多次这时候流量瓶颈主要看内部 SRAM 大小而不是 buffer 是不是共享。也就是说共享 buffer 对“读流量”的影响本来就有限不要指望它把这一项也压下来。3.2 ISP 输出格式和 NPU 输入格式打架软件转换又把流量加了回去这是我在实际项目里遇到的最常见元凶。Camera 的 ISP 通常输出 NV12、NV21 或 YUV422而 NPU 模型输入大多要求 RGB888 planar或者某个固定尺寸。两者一旦不一致共享 buffer 就失去了“格式直通”的前提。如果 NPU 的 DMA 引擎不支持 inline 缩放或格式转换驱动几乎都会选择用 CPU 做一次软件转换。转换过程就是读 NV12 数据逐像素转成 RGB再写到另一块 buffer 里。省掉的 A→B 拷贝被这个“格式转换 写中间结果”完全抵消了。很多所谓“共享 buffer 零拷贝方案”跑起来之后DDR 流量里有一大半是 CPU 格式转换贡献的。检查方法很简单把 ISP 输出格式和 NPU 需要的输入格式放在一起对比。一旦不一致先查驱动里有没有对同一块内存做逐像素读写。有的话再怎么调 buffer 共享都白搭。3.3 cache 一致性处理不当回写流量一笔也没少共享 buffer 意味着同一块 DDR 内存要被 ISP DMA 写、NPU DMA 读如果中间 CPU 也碰过这块数据cache 一致性问题就来了。最容易想到的粗暴解法是整块 buffer 用 non-cacheable 映射但 CPU 访问会很慢。还有一种常见做法是每次投递前对整个 buffer 做 cache clean invalidate。这里的坑在于cache clean 本身会把脏的 cache line 写回 DDR。如果 buffer 比较大这笔回写流量一点不亚于一次显式拷贝。我见过有人图省事在投递点直接调了 clean_all整个 D-cache 全部刷一遍DDR 写带宽瞬间飚到吓人。后来改成只针对当前 buffer 地址范围做 clean流量才回到正常水平。正确做法是配置阶段用 CPU 访问数据搬运阶段 CPU 完全不碰 buffer。数据面 buffer 映射成 Write-Back地址按 cache line 对齐。如果有硬件一致性总线DMA 写入后会自动 invalidate 相关 cache如果没有就只能按地址范围做一次精确的 clean别动不动全缓存扫描。3.4 buffer 轮转、帧率提升和统计口径把收益稀释成噪声还有几个容易忽略的细节。第一个是帧率。为了并行 ISP 和 NPU原来系统里会用三份 buffer 做轮转共享 buffer 之后 buffer 数量减少如果驱动顺手把帧率往上调了一档每帧带宽省下来了每秒总带宽却持平了。第二个是统计点。有的监控工具给的是 L3 或 AXI 总线流量有的是 DDR 控制器侧的 PHY 层流量两者之间差着 ECC、bank precharge、refresh 这些开销。共享 buffer 省的是 AXI 层的数据字节数可如果盯的是 DRAM PHY 层的计数器涨落被底层效率噪声覆盖看起来就是没降。第三个是页表散列。共享 buffer 如果是普通内存物理页分散SMMU/IOMMU 映射页粒度过小DMA 会产生大量地址转换和行激活开销。传输效率下降不体现为“流量减少”而是体现为“同样字节数花了更多总线周期”表现出来还是带宽没降。所以共享 buffer 最好配合 CMA 连续内存或者大页映射否则 DMA 性能还会打折。4. 正确测量 DDR 流量别再用系统级带宽表自欺欺人4.1 DDR 控制器的计数器和 AXI 端口的计数器不是一回事想验证共享 buffer 有没有效果必须把测量点选对。我的建议是测 DDR 控制器统计到的读写字节数或者 NoC/CCI 使能总线上的端口计数而不是只看系统级带宽监控工具给的百分比。两者之间的换算关系不是简单等号DRAM 控制器的计数包含刷新和 ECC 这类带外流量如果拿它当业务流量看会高估不少。可以试试这样采集perf stat -e ddr_read_bytes,ddr_write_bytes -a -- sleep 5或者读 SoC 提供的 memc/debugfs 节点。具体路径每个平台不一样重点是把“读”和“写”拆开统计。共享 buffer 通常先体现在写流量减少上因为少了一次 A→B 拷贝的写读流量也要到 NPU 真正不再额外搬中间 buffer 时才会降。只盯着总带宽百分比的人很难定位到具体是哪一端没省下来。4.2 有效对照实验三种模式三个数据要判断共享是否生效不能只测一个改完的形态要做对照。我当时设计了三个运行模式模式执行方式预期 DDR 读写模式 A传统两次拷贝ISP 写 buffer ACPU 拷贝到 BNPU 读 B读写都高模式 B共享 buffer但 ISP 输出格式与 NPU 输入不一致驱动软件转换读写几乎不降模式 C共享 buffer且 ISP 输出格式与 NPU 输入完全一致读写明显下降实际测出来模式 A 和模式 B 的数字非常接近只有模式 C 能看到可观测的下降。这说明共享 buffer 的价值不是自动到来的你必须把驱动里所有“额外处理”全部拆干净它才会从账面上体现出来。4.3 观察哪些字段能确认收益除了总读写字节我建议同时记录四个数据每帧 DDR 读字节数、每帧 DDR 写字节数、cache clean/invalidate 的次数、DMA burst 长度分布。如果读流量降了写流量没降重点查 cache 回写如果读写都没降优先查驱动里的隐藏 copy如果整体没降但推理延迟下降了说明收益被帧率或并行度吃掉了这时候要重新评估优化目标——也许你本来就不需要压流量而是需要压延迟。5. 如果共享 buffer 救不了带宽真正的杀招在这里5.1 像素格式和分辨率才是带宽的根带宽最敏感的因素是字节数和访问次数而不是 buffer 共享本身。共享的收益上限是“减半”但如果 ISP 输出的分辨率远大于模型输入或者格式本身太占空间减半之后依然不够用。最直接的手段是让 ISP 输出分辨率贴近模型输入利用 binning 或者裁剪把数据量降下来。一个 1080P NV12 是 3.1MB如果模型输入只需要 640×640那么 ISP 直接输出 640×640 时一帧数据只有约 1MB比任何 buffer 优化都狠。其次是尽量选择 ISP 原生支持的、NPU 也直接认的格式。NV12 的效率通常比 RGB888 高但如果模型只认 RGB硬让 ISP 输出 RGB 并用硬件 DMA 直读可能比“ISP 输出 NV12 CPU 转 RGB”的总流量还要划算。这个取舍要整体算一遍不能只看格式大小。5.2 绕开 DDR片内私有通路才是终极方案共享 buffer 再怎么优化都是在 DDR 上来回搬数据。真正的质变是让数据根本不经过 DDR。部分机器视觉 SoC 上存在 ISP 到 NPU 的私有数据通路ISP 输出可以直接进 NPU 的行缓存或内部 SRAMDDR 侧只剩下 NPU 推理结果的写回。这种模式下Camera 到 NPU 的 DDR 流量几乎归零和共享 buffer 不是一个量级。代价是驱动复杂度高ISP 和 NPU 需要在时钟、帧同步、数据格式上做深度绑定而且很多平台该通路只支持特定分辨率和格式。如果你的平台有这条路值得投入资源去打通没有的话共享 buffer 只能是第一步别指望它一劳永逸。5.3 QoS 仲裁总量不降也可以让关键路径不卡有时候你费尽力气把平均 DDR 带宽降下来了用户感知到的卡顿却没有改善原因是问题不在平均带宽而在瞬时峰值。NPU 读数据时如果被显示控制器或别的 DMA 抢了优先级推理时间抖动会非常明显。这时候比起压流量配置 DDR 的 QoS 仲裁权重更有效给 NPU 端的数据通路提高优先级给 ISP 输出配置稍低一点的权重保证关键时刻 NPU 的读取不被插队。我在实际项目里调整了 QoS 寄存器后推理延迟抖动下降了接近一半即便平均带宽纹丝不动。这提醒我优化目标要先搞清楚到底是要降总量还是要降延迟。最后说一点我自己的体会。共享 buffer 本质上不是带宽优化手段它首先解决的是内存占用翻倍和拷贝延迟的问题。它的直接收益是内存占用减半、端到端延迟变低而 DDR 流量的改善只有在业务链路里确实存在 A→B 拷贝、且格式链路完全打通的前提下才会明显。以后再遇到“共享 buffer 了 DDR 流量为什么没降”先别怀疑工具问题按物理地址、驱动 trace、DMA descriptor 一级级查下去答案大概率不在共享逻辑本身而在数据通路里某个被忽略的副本上。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑