嵌入式c代码的一些优化方法
- 编译器优化选项
- -flto 链接时优化:编译时生成GIMPLE,而后链接时视同一个编译单元进行优化。使用此选项进行优化时,须要GCC插件,在生成库文件时须要指定。若是生成库时指定了该选项,则能够在链接该库时进行优化。
- -On 编译优化级别:有0,g,1,2,3,s,fast六个级别,0级不优化,fast最高(但有可能会出现问题),3和s可能须要实验选择一个能够接受结果。g优化级别能够用来调试,从而初步检查在更高级别优化时会出现的错误。
- -ffunction-sections -fdata-sections和链接时的选项--gc-sections配合能够去掉未使用的函数和数据,这个选项包含在了-On选项中。另外:-fdata-sections在avr-gcc编译器中若是使用变量属性io和address时会出现问题。
- 使用函数属性优化:例如__attribute__((flatten)),这个属性能够将函数内部的调用优化成内联函数;__attribute__((always_inline))能够将函数内联到其余函数中;__attribute__((optimize("Os")))能够指定函数的优化级别,这个级别能够和项目的优化选项不同,从而避免在特定级别优化时出现的错误。
- 代码的优化
- 宏:使用宏并配合-On和-flto能够在编译时得到良好的优化结果,但也不必定,例如
// 使用宏进行优化时没有下面的代码好。 #define sprtbl ((uint8_t[][2]){{4, 2}, ..., {3, 128}}) // static uint8_t sprtbl[][2] = { // {4, 2}, {0, 4}, {5, 8}, {1, 16}, {6, 32}, {2, 64}, {3, 128}, // }; static inline uint8_t spr(uint32_t baud) { uint8_t m = F_CPU / baud; for (int i = 0; i < 7; i++) { if (m < sprtbl[i][1]) { return sprtbl[i][0]; } }; return 3; }
2. 如同上面的代码,使用inline定义函数能够得到等同于宏的优化效果函数
3. 若是定义io指针结构,则使用const限定会得到较好的优化结果,例如,优化
typedef struct usart_io_s { volatile uint8_t* ubrrl; volatile uint8_t* ubrrh; ... // other io } usart_io_t; #define usart0_io { &UBRR0L, ... } #define usart1_io { &UBRR1L, ... } volatile struct usart_buffer_s { ... } rx[USART_COUNT] = {}, tx[USART_COUNT] = {}; const struct usart_handle_s { usart_io_t io; volatile struct usart_buffer_s* rx; volatile struct usart_buffer_s* tx; ... // others fields } usart[USART_COUNT] = { {usart0_io, &rx[0], &tx[0]}, {usart1_io, &rx[1], &tx[1]}, }; // 当rx,tx使用指针时,usart[]结构能够得到优化。例如访问io成员时,能够将其优化为io指令。
4. 函数内使用const定义一些内部常量ui
[code A] void port_init(int id, int io) { const port_io_t p = pio[id / 8]; if (io == 1) { *p.ddr |= ...; } else { *p.ddr &= ...; } } 相比下面的代码,能够得到更多的优化 [code B] void port_init(int id, int io) { if (io == 1) { *pio[id / 8].ddr |= ...; } else { *pio[id / 8].ddr &= ...; } } 上面的代码中pio即便声明为const,A也会得到比B更多的优化(有前提条件,见后面的代码); 例如: void main() { port_init(PA0, OUTPUT); ... port_init(PA7, OUTPUT); } 若是你调用port_init少于3个(avr-gcc 5.4),则A与B没什么区别,若是大于3个,则A,B区别明显。(为何3个?忽然间以为gcc的编写者是我道门中人啊,三生万物啊)
5. 位域优化,位域能够简化对IO寄存器功能位的访问,一个赋值语句等同于读写改三个语句,固然带来的问题就是多任务环境下的数据同步问题。若是屡次访问同一(volatile)寄存器上的不一样位域,则会生成重复访问同一寄存器的汇编代码,下面给出一个方案,能够将代码优化成一次访问spa
将IO寄存器定义成联合体,以下: union sreg_t { uint8_t i8; struct { unsigned c : 1; //!< carry flag. unsigned z : 1; //!< zero flag. unsigned n : 1; //!< negative flag. unsigned v : 1; //!< two's complement overflow flag. unsigned s : 1; //!< sign bit. unsigned h : 1; //!< half carry flag. unsigned t : 1; //!< bit copy storage. unsigned i : 1; //!< global interrupt enable. }; }; 使用时, sreg_t sreg = {}; sreg.c = 1; sreg.i = 1; // 真正的赋值,SREG为状态寄存器指针 SREG = sreg.i8;
以上方法适用于GCC插件