#define 与 static const 的优化(在 avr-gcc 中)

Optimization of #define vs static const (in avr-gcc)

虽然我欢迎在一般范围内回答这个问题,但我主要要求 avr-gcc 使它不要太宽泛。

我看了一些问题,尤其是 this and this 一个。他们主要关注语义差异,例如static const 不能用来代替常量表达式。但是,虽然泛泛地谈内存分配,却不谈优化。

我们来看一个例子:

typedef struct {
    GPIO_TypeDef *port;
    uint8_t  pin;
} gpio_pin_t;

static inline void gpio_pin_set(gpio_pin_t gpio, bool set) {
    if(set) GPIO_SetBits  (gpio.port, 1 << gpio.pin);
    else    GPIO_ResetBits(gpio.port, 1 << gpio.pin);
}

//elsewhere including above definitions

static const gpio_pin_t gpio_pin = {GPIOE, 8};

void some_function(bool enable) {
    gpio_pin_set(gpio_pin, enable);
}

如您所见,我正在使用结构,所以第三种既定方式(枚举)在这里不是一个选项

我可以期望 gcc 优化内联函数中的 gpio.portgpio.pin 访问吗?如果不是,为什么会这样,gcc 在看到 const 时会应用其他优化吗?

总的来说,通过使用 static const 变量而不是定义,我在优化方面失去了什么, 尤其是超越简单的整数常量?

这取决于编译器实现。

如果您从不获取它的地址并且不导出符号(对于 C,它是默认的,因此您必须使用 static),那么优化器应该启动并优化它。对于简单类型(intfloat),您可以合理地期望它能够全面工作,但对于 structs - 最好自己检查一下编译器做了什么。对于像我的 GCC 这样的简单结构,优化它,消除结构并直接传递它的值,将常量加载到 CPU 寄存器。对于较大的结构,它可能会认为不值得。

您可以生成要检查的代码的汇编列表:

avr-gcc -O<opt> -S somefile.c

gcc -O<opt> -S somefile.c

不要忘记优化级别!

使用 #define 很糟糕,因为预处理器真的很笨——它只是在编译之前逐字替换代码。使用 const 可以获得更好的类型安全性。考虑这个例子:

#define BYTE_VALUE 257
static const uint8_t sc_value = 257; // at least will show warning
int my_byte = BYTE_VALUE; // no warning, but obviously it's a bug!

关于抽象:

为什么要将如此简单的东西淹没在大量的抽象层中?您可以放心地假设每个 C 程序员都知道

的含义
PORT |= (1<<pin);
PORT &= ~(1<<pin);

就 C 程序员而言,这是代码的可读性。通过将此代码隐藏在抽象层中,您的代码对 C 程序员来说 可读性较差 ,尽管它对非程序员来说可能更易读。但是你希望前者阅读你的代码,而不是后者。

以上也是最快的,就效率而言。编译器很可能会将此类代码直接翻译成单个位 set/bit 清晰的汇编程序指令。

所以我的建议是抛弃所有的抽象。您不想对嵌入式 C 程序员隐藏硬件。您需要向他们隐藏的是具体的硬件实现,这样他们就不需要为每个新项目重新发明轮子。

其中一个抽象是在编写独立于硬件的 API 层时。例如void led_set (bool lit);。此功能将点亮板上的 LED。您可以将它放在抽象文件 led.h 中,该文件没有对应的 .c 文件。因为 .c 文件仅为您的特定项目实现:在 my_led.c 中,您将拥有实际的实现,它直接访问 GPIO 寄存器、设置数据方向和拉电阻寄存器、处理信号极性等。


关于您的具体问题:

不能保证 GCC 会按照您的预期内联该函数:inline 关键字几乎已经过时,因为在决定何时内联函数时,如今的编译器比程序员聪明得多。我会说这很有可能,因为您在启用最大优化的情况下进行编译。找出答案的唯一方法就是尝试。

但是编译器是否内联这个函数并不重要。您可能永远不会有如此极端的实时要求,以至于函数调用开销会影响您的程序。我们在这里谈论的是几十纳秒:即使是您电路板上的数字电子集成电路也不会足够快地响应那些额外的 CPU 滴答来产生影响。

我每天都使用基于 MCU 的实时嵌入式系统,即使在这些系统中,您也很少会遇到像这样的极端代码优化很重要的情况。如果这样做,您将使用 DSP,而不是常规 MCU,最肯定不是 AVR。

更重要的是,您的 static const 将常量的范围缩小到本地文件,因此 none else 需要自己关心它,也不会弄乱全局名称空间。这是良好的编程习惯,良好的编程习惯在 10 次中有 9 次胜过手动代码优化。