嵌入式科普(50)SEGGER RTT vs UART打印速度深度分析

一、目的

1、分析嵌入式调试中printf输出的性能瓶颈

2、对比SEGGER RTT与传统UART(轮询/中断/DMA)的耗时差异

3、说明本文内容与具体芯片无关,适用于所有支持J-Link的平台

二、核心结论

以典型调试日志printf("val=%d\n", 123);(15字节)为例:

测试条件:STM32F407 Cortex-M4 @ 168MHz,UART 115200bps,J-Link Pro @ 50MHz SWD

方式 总耗时 格式化占比 传输占比 瓶颈所在
UART轮询 ~1060μs ~1.5% ~98.5% 等待串口移位发送
UART中断 ~1065μs ~2% ~98% 等待串口移位发送
UART DMA ~1080μs ~3% ~97% 串口移位发送+占用总线
RTT ~12μs ~83% ~17% 格式化计算

推算依据

  • UART传输时间 = 15字节 × 10bit/字节 ÷ 115200bps = 1.3ms(物理限制)

  • RTT传输时间 = 基于SEGGER官方"一行文本<1μs",15字节约2μs(内存拷贝)

  • 格式化时间 = 基于DigiKey测试,简单整数格式化约65-365μs(取决于时钟频率)

一句话总结

  • UART/DMA

    :98%时间花在串行移位发送(115200bps物理限制)

  • RTT

    :瓶颈在格式化,传输仅2μs(一次内存拷贝)

性能对比图

各阶段时间占比详解

测试条件:STM32F407 @ 168MHz,UART 115200bps,J-Link Pro @ 50MHz SWD

方式 格式化时间 格式化占比 传输时间 传输占比 总耗时
UART轮询 ~17μs ~1.5% ~1042μs ~98.5% ~1060μs
UART中断 ~22μs ~2% ~1042μs ~98% ~1065μs
UART DMA ~36μs ~3% ~1041μs ~97% ~1080μs
RTT ~10μs ~83% ~2μs ~17% ~12μs

推算依据

  • UART传输 = 15 × 10 ÷ 115200 = 1.3ms(含起始/停止位)

  • RTT传输 = SEGGER官方"一行文本<1μs",15字节≈2μs

  • 格式化时间 = DigiKey测试数据,48MHz时钟下约65-68μs,168MHz下约10-20μs(按比例推算)

关键发现

  • UART系列

    :格式化仅占1.5%-3%,98%以上时间用于串行传输

  • RTT

    :格式化占83%,传输仅占17%,瓶颈完全反转

  • RTT传输时间

    :仅2μs(一次内存拷贝),是UART的1/500

三、核心原因分析

3.1 为什么UART慢?

复制代码
printf("val=%d\n", 123);  // 15字节

[格式化 ~15μs] → [串口发送 ~1042μs]  ← 98%时间在这里
                      ↑
                物理限制!
                115200bps = 每秒11520位
                15字节 = 120位
                120 / 115200 = 1.04ms

串口是串行移位传输

复制代码
字节0: [起始][bit0][bit1]...[bit7][停止] → ~87μs
字节1: [起始][bit0][bit1]...[bit7][停止] → ~87μs
...
字节14: [起始][bit0][bit1]...[bit7][停止] → ~87μs
───────────────────────────────────────────
总计: 15 × 87μs = 1305μs ≈ 1.3ms

:115200bps下,每个字节传输时间 = 10bit ÷ 115200bps = 86.8μs

3.2 DMA为什么也慢?

复制代码
DMA传输15字节:
[DMA读内存] → [DMA写UART FIFO] → [UART移位发送 1ms]
     ↓               ↓                    ↓
   占用总线        占用总线           1ms串行传输

DMA的问题

  • 传输时间不变

    :仍需1ms(波特率限制)

  • 占用总线

    :其他DMA/总线主设备需等待

  • DMA解决的是CPU占用 ,不是传输速度

3.3 RTT为什么快?

复制代码
printf("val=%d\n", 123);  // 15字节

[格式化 ~10μs] → [内存拷贝 ~2μs] → [完成]
                       ↑
                 这是内存操作!
                 32位总线,一次拷贝4字节
                 15字节 ≈ 4次内存写入
                 每次 ~0.5μs

推算依据:SEGGER官方"一行文本<1μs"是指纯内存拷贝,不含格式化。15字节在32位总线上约4次写入,每次~0.5μs。

RTT的本质

  • 一次内存拷贝

    ,不是串行传输

  • 内存带宽 >> 串口带宽(1000倍+)

  • 不占用外设总线,不影响其他DMA

3.4 本质区别

方式 传输本质 耗时决定因素
UART轮询 串行移位 波特率 × 字节数
UART中断 串行移位 波特率 × 字节数
UART DMA 串行移位 波特率 × 字节数
RTT 内存拷贝 内存带宽(可忽略)

四、引用数据

4.1 SEGGER官方性能数据

引用自 SEGGER RTT官方文档:

速度对比(STM32F407 Cortex-M4 @ 168MHz)

测试条件:SEGGER官方测试,J-Link Pro @ 50MHz SWD,RTT缓冲区512字节

调试方式 输出1KB耗时 相对速度 传输机制 数据来源
UART 115200 ~87ms 1x 串行移位 计算值¹
UART 921600 ~11ms 8x 串行移位 计算值¹
SWO (SWV) ~5ms 17x 串行跟踪 SEGGER图表估算
Semihosting ~200ms 0.4x 调试器中断 典型值²
SEGGER RTT ~0.5ms 174x 内存拷贝 SEGGER官方³

注释

  1. 计算公式:数据量 × 10bit/字节 ÷ 波特率 × 1000ms

  2. Semihosting需要调试器中断,速度取决于调试器实现

  3. SEGGER官方:"average line of text can be output in one microsecond or less"

关键结论 :RTT通过调试器直接读取内存,绕过串行传输瓶颈,比标准UART快174倍

RTT性能指标

数据来源:SEGGER官方文档

指标 数值 说明 数据来源
写入速度 > 2 MB/s 远超UART的11.5KB/s SEGGER官方
典型延迟 < 1 μs 内存操作,几乎无延迟 SEGGER官方
CPU占用 < 1% 仅写入缓冲区 SEGGER官方
代码大小 ~500 Bytes ROM占用 SEGGER官方
RAM占用 24B + 24B/通道 控制块+缓冲区 SEGGER官方

官方说明 :RTT利用调试器的内存访问能力,在CPU运行时实时读取数据,无需停止目标程序。平均一行文本输出在1微秒或更少 ,基本上只是一次memcpy()的时间。

4.2 不同数据量性能对比

测试条件:UART 115200bps/921600bps,RTT @ 2MB/s(基于SEGGER官方数据)

数据量 UART 115200 UART 921600 RTT RTT加速倍数
15字节 ~1.3ms ~0.16ms ~0.008ms 160x
100字节 ~8.7ms ~1.1ms ~0.05ms 174x
1KB ~87ms ~11ms ~0.5ms 174x
10KB ~870ms ~110ms ~5ms 174x

计算公式

  • UART时间 = 数据量 × 10bit/字节 ÷ 波特率 × 1000ms

  • RTT时间 = 数据量 ÷ 2MB/s × 1000ms(基于SEGGER官方">2MB/s")

  • 加速倍数 = UART 115200时间 ÷ RTT时间

4.3 RTT速度优势的底层原因

说明:基于硬件架构分析

对比维度 UART RTT 差异倍数 数据来源
传输机制 串行移位(1bit/时钟) 内存拷贝(32bit/时钟) 32x 硬件架构分析
传输介质 UART外设 系统总线 - 硬件架构分析
带宽上限 11.5KB/s (115200bps) >2MB/s 174x SEGGER官方
CPU参与 全程/部分 仅写入 - SEGGER官方
总线占用 外设总线 系统总线 - 硬件架构分析

4.4 RTT格式化函数优化

说明:以下为基于SEGGER官方API设计的一般性分析,非精确测量值

RTT的printf实现比C标准库更快:

函数 调用链 特点
C库printf printf → vprintf → vfprintf → _write 通用实现,支持完整格式规范
RTT printf SEGGER_RTT_printf → _StoreChar 精简实现,直接写入内存

优化点

  • 减少函数调用层级

  • 避免系统调用开销(无需经过操作系统)

  • 直接写入内存,无阻塞等待

  • SEGGER官方:精简版printf不依赖堆,仅使用可配置的栈空间

4.5 直接字符串输出(最快)

对于固定日志,可跳过格式化,直接输出:

复制代码
// 需要格式化:约12μs(含格式化开销)
SEGGER_RTT_printf(0,"val=%d\n",123);

// 直接输出:约2μs(仅内存拷贝)
SEGGER_RTT_WriteString(0,"Entering function\n");

说明:直接字符串输出跳过格式化步骤,速度更快。基于SEGGER官方"一行文本<1μs"的描述,短字符串约1-2μs。

五、RTT工作原理

5.1 数据流对比

5.2 时间线对比

PCJLinkUARTCPUPCJLinkUARTCPUUART方式 (1059μs)RTT方式 (12μs)格式化 (17μs)写入数据串行移位 (1042μs)逐位传输格式化 (10μs)内存拷贝 (2μs)读取内存 (后台)USB传输 (后台)

5.3 RTT内部结构

与UART的本质区别

对比项 UART RTT
传输方式 CPU驱动外设串行发送 J-Link主动读取内存
CPU参与 全程/部分 仅写入缓冲区
等待时间 等待发送完成 写入即返回
硬件依赖 UART外设+USB转串口 J-Link调试器
数据流向 单向(TX→RX) 双向(上行+下行)

六、RTT的缺点与局限性

6.1 硬件依赖

缺点 说明 影响
需要J-Link调试器 必须购买SEGGER J-Link硬件 成本增加(正版J-Link $400+)
调试接口占用 使用SWD/JTAG调试接口 无法同时用于其他调试工具
仅支持特定内核 Cortex-M/A/R、RISC-V、Renesas RX 不支持所有MCU平台

6.2 使用场景限制

缺点 说明 影响
仅限开发调试 生产环境无法使用(无J-Link) 需要维护两套日志代码
调试器必须连接 断开J-Link后无法输出 不适合独立运行场景
缓冲区溢出风险 非阻塞模式可能丢数据 高速输出时需注意缓冲区大小

6.3 与UART对比的局限性

对比项 UART RTT
硬件成本 无额外成本(芯片内置) 需要J-Link ($400+)
使用场景 开发+生产 仅开发调试
独立运行 支持(USB转串口) 不支持(需调试器)
多设备调试 每个设备需串口 每个设备需J-Link
接口占用 专用UART引脚 调试接口(SWD/JTAG)

6.4 成本对比

项目 UART方案 RTT方案
硬件成本 USB转串口 ~$5 J-Link ~$400+
引脚占用 2个(TX/RX) 2-4个(SWD/JTAG)
软件成本 免费 免费(RTT源码开源)
适用场景 开发+生产 仅开发

建议:对于个人开发者或小团队,可考虑J-Link EDU版本(~$60),功能相同但仅限教育用途。

七、实际应用建议

7.1 何时使用RTT

场景 推荐方式 原因
开发调试 RTT 速度快100倍,不影响时序
生产环境 关闭日志 避免任何性能影响
无J-Link UART DMA CPU占用低,但速度不变

7.2 优化建议

1、关键路径使用直接字符串

复制代码
SEGGER_RTT_WriteString(0,"Entering function\n");

2、使用条件编译

复制代码
#ifdefDEBUG
#defineDBG_LOG(...)SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#defineDBG_LOG(...)
#endif

八、总结

8.1 时间分布对比

测试条件:STM32F407 @ 168MHz,UART 115200bps,15字节printf输出

复制代码
UART: [格式化 ~1.5%] → [串行移位发送 ~98.5%]  ← 98%时间在这里
                              ↑
                        物理限制,无法加速

DMA:  [格式化 ~3%] → [DMA启动] → [串行移位发送 ~97%]  ← 仍需1ms
                                    ↑
                              总线被占用

RTT:  [格式化 ~83%] → [内存拷贝 ~17%]  ← 仅2μs!
                           ↑
                     内存操作,极快

8.2 核心结论

对比维度 UART/DMA RTT 差异原因
瓶颈位置 串行传输(~98%) 格式化(~83%) 传输机制不同
传输机制 串行移位(1bit/时钟) 内存拷贝(32bit/时钟) 32倍并行度
带宽上限 11.5KB/s (115200bps) >2MB/s 174倍
物理限制 波特率×字节数 内存带宽(可忽略) 本质差异
总线影响 占用外设总线 不占用 RTT更优
硬件成本 无额外成本 需要J-Link ($400+) RTT缺点

8.3 最终结论

RTT比UART快100-174倍,即使使用DMA也无法缩小差距,原因如下:

  1. 核心差异是数据传输机制

    • UART:串行移位,1bit/时钟,受波特率限制

    • RTT:内存拷贝,32bit/时钟,受内存带宽限制

  2. printf格式化占比很少

    • UART:格式化仅占1.5%-3%,**98%时间用于传输**

    • RTT:格式化占83%,传输仅占17%,瓶颈反转

  3. DMA无法解决根本问题

    • DMA解决的是CPU占用,不是传输速度

    • 串行移位时间不变(仍需1ms@115200bps)

    • DMA还占用总线,可能影响其他外设

  4. RTT的物理优势

    • 内存带宽 >> 串口带宽(1000倍+)

    • 不占用外设总线

    • J-Link后台读取,不影响CPU运行

RTT的主要缺点

  1. 硬件成本高

    :需要购买J-Link调试器

  2. 调试器必须连接

    :断开J-Link后无法输出

  3. 平台限制

    :仅支持Cortex-M/A/R、RISC-V、Renesas RX

一句话总结 :RTT快是因为内存拷贝 vs 串行移位 ,这是物理机制的本质差异,不是软件优化能弥补的。但需要权衡性能提升硬件成本

九、参考资料

资源 链接
Faster printf Debugging (Part 1) https://www.digikey.ch/fr/maker/tutorials/2025/faster-printf-debugging-part-1
SEGGER RTT官方 https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/
RTT源码 https://github.com/SEGGERMicro/RTT