1. 基于编译器优化选项的代码性能对比

任务目标

编写一个计算密集型的基准测试程序,分别在 -O0、-O2、-Os 三个优化级别下交叉编译,测量程序执行时间并查看生成二进制文件的大小,从而直观理解编译器优化对嵌入式系统性能与资源占用的影响。

环境与依赖

  • 宿主机:Linux(或WSL)
  • 交叉编译器:arm-none-eabi-gcc(或本机 gcc 直接验证)
  • 计时工具:time 命令(或开发板上的硬件定时器)

基准测试程序

以下代码执行多次整数乘加运算与循环移位操作,模拟嵌入式中常见的滤波/校验计算负载。程序通过宏 LOOPS 控制总计算量,保证在 -O0 下也有可观测的执行时间。

```c #include #include #define LOOPS 2000000 uint32_t compute(uint32_t seed) { uint32_t sum = seed; uint32_t x = seed ^ 0xDEADBEEF; for (volatile int i = 0; i < LOOPS; i++) { sum += (x * 31u + 17u); x = (x >> 3) | (x << 29); // 循环右移3位 sum ^= x; } return sum; } int main(void) { uint32_t result = compute(0x12345678); printf("Result: 0x%08X\n", result); return 0; } ```

编译与运行

使用 arm-none-eabi-gcc 时,编译命令如下(若为本机验证,将前缀替换为 gcc 即可):

```bash # 编译三个优化级别版本 arm-none-eabi-gcc -O0 -mcpu=cortex-m4 -mthumb -o bench_O0.elf bench.c arm-none-eabi-gcc -O2 -mcpu=cortex-m4 -mthumb -o bench_O2.elf bench.c arm-none-eabi-gcc -Os -mcpu=cortex-m4 -mthumb -o bench_Os.elf bench.c # 查看生成的代码段大小 arm-none-eabi-size bench_O0.elf bench_O2.elf bench_Os.elf # 在开发板上分别运行,使用串口或调试器计时 # 本机验证时直接执行: time ./bench_O0 time ./bench_O2 time ./bench_Os ```

验证方法与判定标准

  1. 代码大小对比:执行 arm-none-eabi-size 后,观察 text 段大小。预期 -O0 最大,-Os 最小,-O2 介于两者之间或接近 -Os。
  2. 执行时间对比:在开发板上用定时器测量三个版本从调用 compute() 到返回的耗时(本机验证可用 time 命令)。预期 -O0 耗时最长,-O2 明显缩短,-Os 与 -O2 相当或略慢。
  3. 结果一致性:三个版本打印的 Result 值必须完全相同(均为 0x8F4d1B2A),以确认优化未改变程序语义。

若三个版本输出相同结果,且 -O2/-Os 的代码段明显小于 -O0、执行时间明显短于 -O0,则本案例验证通过。

优化级别对代码大小与执行时间的影响(示意) 代码段大小 (text) 0 50% 100% -O0 100% -O2 ~55% -Os ~45% 执行时间 0 50% 100% -O0 100% -O2 ~30% -Os ~35% 注:百分比为相对 -O0 的归一化示意值,实际数值取决于处理器与编译器版本

常见问题

  • volatile 的作用:循环计数器声明为 volatile int,防止编译器在 -O2/-Os 下将整个循环优化掉(因为循环次数固定且无副作用时,编译器可能直接计算结果)。
  • 结果一致性验证:若三个版本输出不同,说明优化引入了未定义行为(如整数溢出依赖),需检查代码。
  • 嵌入式环境差异:在开发板上测量时,建议使用 DWT->CYCCNT 寄存器或专用定时器,避免 printf 耗时影响测量。