从printf获得最佳性能

首先,我们研究了printf的基本问题,然后研究了一些可用于从中获得最大性能的技术。

了解printf的问题

使用printf会带来一些开发人员经常忽略的问题。首先,开发人员必须引入标准的C库,这无疑会增加ROM和RAM的使用。其次,每次使用打印雄蕊时,系统都会阻塞,直到传输完所有字符为止,这可能导致实时性能显着下降。例如,输出一个简单的字符串,例如“ Hello World!”。在9600处被打印出UART(仍然很常见)。我在STM32上执行了一个简单的时序测量,如图1所示,该字符串格式化并打印到终端需要12.5毫秒的时间。

图1 –打印“ Hello World!”

添加任何字符串格式会使情况更糟!使用printf(“ System state is%d”,State)将系统状态打印到终端会导致字符串格式化和传输时应用程序延迟21毫秒。有人可能会争辩说,以9600的波特率运行是荒谬的,但即使将其提高到115200,也仍然分别需要1.05和1.75毫秒来传输这两个消息。对于最低限度的有用信息,很多处理器带宽和潜在的实时性能都会受到影响。

性能技术#1 –创建无阻塞的printf

我在野外遇到的每个printf版本都是阻塞类型。调用printf后,应用程序将停止执行,直到成功传输每个字符为止。效率低下!一种替代方法是创建一个非阻塞版本。非阻塞的printf版本将

  • 格式化字符串
  • 将格式化的字符串填充到传输缓冲区中
  • 启动第一个字符的传输
  • 让中断服务程序处理发送缓冲区中的其余字符
  • 继续执行代码。

对于无阻塞printf来说,最大的亮点是建立时间,在STM32上,波特率为9600,我发现它在0.8到1.8毫秒之间变化。在初始设置时间之后,发送中断大约每隔一毫秒发生一次,并且需要35微秒的时间才能将下一个字符填充到UART发送寄存器中,然后再恢复工作。图2显示了周期性中断以及中断执行时间。请记住,在这种情况下,执行时间不包括小于25个时钟周期的中断开销。

图2 –无阻塞printf性能

性能技巧2 –提高波特率

我很震惊,如此众多的开发人员仍然会将其UART默认为9600。今天的串行硬件可以处理1 Mbps或更高的波特率!有时,我会遇到一个大胆的人,将波特率设置为115200。除非在运行时钟方面存在与电气或硬件相关的潜在问题,否则将波特率设置为1 Mbps并以为了尽可能减少实时性能问题,请尽可能快地进行操作。 “ Hello World!”的原始阻止printf只会阻塞120微秒。比12.5毫秒要可接受得多。

表演技巧3 –使用SWD

现代微控制器在开发芯片时会考虑到printf性能问题。例如,利用ARM Cortex-M部件的调试功能的开发人员可以跳过UART,并使用内部调试模块将printf消息通过调试器传输回IDE。以这种方式跳过UART不仅可以节省设置,而且内部硬件机制可以最大程度地减少软件开销!内部缓冲区中填充了该消息,并且调试硬件自动处理到调试探针的传输,这对应用程序的实时性能的影响最小。

结论

很少有开发人员会放弃他们最喜欢的,经过尝试的和真正的printf调试技术。在当今的现代微控制器硬件中,存在多种选择来提高printf的性能和效率,从而最大程度地减少对实时性能的影响。对于希望自己尝试这些改进的开发人员,我为STM32整理了一个Keil项目,演示了如何使用这些技术。可以找到该项目 这里 。 (检查uart_app.c以获取最有趣的代码)。

One thought 上 “从printf获得最佳性能”

  1. 无阻塞的printf()还有另一种方法。通常printf()调用putchar(),因此瓶颈在这里,除了printf()需要进行格式化的时间。
    您要做的就是用一个写入环形缓冲区的版本覆盖putchar(),然后让相关的中断例程进行实际的传输。因此可以使用库标准的printf(),并且避免了sprintf()和静态打印缓冲区的解决方法。
    自8051年以来,这种方法一直对我有效。
    此外,printf()不仅适合调试-
    — Hans-Peter

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *

该网站使用Akismet减少垃圾邮件。 了解如何处理您的评论数据.