我在用IOs调试关键代码时遇到了一个难题:
这两种功能之间最快的是什么?
我的CPU将在哪个功能中花费更少的时间?
答: CPU读取外设寄存器并写入外设寄存器。
void d_toggle_pin(void)
{
NRF_P1->OUT ^= 1 << Debug_Pin;
}B: CPU读取RAM变量并写入外围寄存器+写入RAM变量
void d_toggle_pin(void)
{
static byte pin_state = 0;
if(pin_state)
{
NRF_P1->OUTCLR = 1U << Debug_Pin;
pin_state = 0;
}
else
{
NRF_P1->OUTSET = 1U << Debug_Pin;
pin_state = 1;
}
}我正在使用nrf52840 (皮质M4 CPU),但是不管实现如何,答案可能是相同的。
发布于 2022-03-02 14:36:31
TL;DR:第一个版本表现更好。
在性能方面的差异是微不足道的。皮层M3和更高版本有简单的分支预测和流水线,但是这不会对这里的简单代码产生很大的影响。当然,第二个版本在分支预测器上可能会稍微粗糙一点,因为它们是两个单独的内存映射寄存器,但两者之间的差别是可以忽略不计的。
如果您坚持将它们进行比较,那么下面是gcc ARM non-eabi -O3的一个小基准,在这里,我替换了寄存器名,并将“调试引脚”作为硬编码常量:https://godbolt.org/z/88vn1EqKj。对分支进行了优化,但第一个版本的性能仍略好一些。
然而,您在这里的首要任务应该是功能和可读性。这两种功能都可以,但如果我要解剖它们.
其他值得注意的事项:
1 << ...在C中总是错误的,您几乎可以肯定永远不要移动有符号的int,这是整数常量1的类型。例如,1 << 31调用未定义的行为。始终使用1u。
为这样一个非常基本的事情编写包装函数,比如设置/清除/切换GPIO引脚已经做了上百次了.没有人能够编写比以下更容易阅读的函数包装器:
reg |= mask (set)reg &= ~mask (clear)reg ^= mask; (toggle)这是惯用的、超快的、超可读的C代码,可以100%的C程序员理解。在查看了数百个失败的、臃肿的GPIO HALs之后,我会自信地说,抽象简单的GPIO可以而且只会导致膨胀。我自己也写了不少这样的书,这总是个错误。
(对于带有一系列路由寄存器、中断处理、奇怪的状态标志等更复杂的GPIO,那么无论如何都要编写一个HAL和一个驱动程序。但并不是为了做简单的端口I/O)
https://stackoverflow.com/questions/71323728
复制相似问题