扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

嵌入式开发中,有哪些优化技巧?

2025/09/19
2061
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

嵌入式开发中,资源永远是稀缺的。内存不够用、运行速度慢、功耗太高...这些问题是不是经常让你头疼?

今天就来分享几个实战验证的代码优化技巧!

1.时间效率优化

避免浮点运算

// 慢速版本:浮点运算
float calculate_voltage(int adc_value) {
    return adc_value * 3.3f / 4096.0f;
}

// 快速版本:定点运算
int calculate_voltage_fast(int adc_value) {
    return (adc_value * 3300) >> 12;  // 除以4096用右移替代
}

减少函数调用

// 慢速版本:频繁函数调用
for(int i = 0; i < 1000; i++) {
    set_led_state(i % 2);
}

// 快速版本:内联展开
for(int i = 0; i < 1000; i++) {
    if(i % 2) {
        GPIO_SetBits(GPIOA, GPIO_Pin_5);
    } else {
        GPIO_ResetBits(GPIOA, GPIO_Pin_5);
    }
}

2. 空间效率优化

选择合适的数据类型

// 浪费内存的写法
struct sensor_data {
    int temperature;     // 实际只需要-40到125
    int humidity;        // 实际只需要0到100
    int pressure;        // 实际只需要300到1100
};

// 节省内存的写法
struct sensor_data_optimized {
    int8_t temperature;   // -128到127,够用
    uint8_t humidity;     // 0到255,够用
    uint16_t pressure;    // 0到65535,够用
};
// 内存节省:从12字节降到4字节

使用联合体节省空间

// 通信协议数据包
typedefunion {
    struct {
        uint8_t header[4];
        uint8_t cmd;
        uint8_t data[32];
        uint8_t checksum;
    } packet;
    uint8_t raw_data[38];
} comm_frame_t;

// 可以按字节访问,也可以按结构访问
comm_frame_t frame;
frame.packet.cmd = 0x01;           // 结构化访问
send_data(frame.raw_data, 38);     // 字节数组访问

3. 用空间换时间

场景

你需要统计一个4位数据(0x0~0xF)中有多少个1,传统方法是用循环遍历每一位。

普通写法

int count_ones_slow(unsigned char data) {
    int cnt = 0;
    unsigned char temp = data & 0xf;
    
    for (int i = 0; i < 4; i++) {
        if (temp & 0x01) {
            cnt++;
        }
        temp >>= 1;
    }
    return cnt;
}

优化写法

// 预先计算好的查找表
static int ones_table[16] = {
    0, 1, 1, 2, 1, 2, 2, 3, 
    1, 2, 2, 3, 2, 3, 3, 4
};

int count_ones_fast(unsigned char data) {
    return ones_table[data & 0xf];
}

性能对比

    传统方法:需要4次循环 + 4次位运算查表方法:仅需1次数组访问

适用场景:复杂计算、三角函数、CRC校验

4. 使用柔性数组

传统指针方式的问题

typedef struct {
    uint16_t head;
    uint8_t id;
    uint8_t type;
    uint8_t length;
    uint8_t *value;  // 指针方式
} protocol_old_t;

问题

    需要两次内存分配内存不连续,访问效率低释放内存容易出错

柔性数组的优雅

typedef struct {
    uint16_t head;
    uint8_t id;
    uint8_t type;
    uint8_t length;
    uint8_t value[];  // 柔性数组
} protocol_new_t;

优势

    一次分配,连续内存访问速度更快内存管理更简单避免内存泄漏风险

使用示例

// 分配结构体 + 数据空间
protocol_new_t *p = malloc(sizeof(protocol_new_t) + data_len);

5. 使用位操作

位域:节省内存的小技巧

管理8个标志位,你会怎么做?

内存浪费的写法

struct flags_waste {
    unsignedchar flag1;  
    unsignedchar flag2;
    unsignedchar flag3;
    unsignedchar flag4;
    unsignedchar flag5;
    unsignedchar flag6;
    unsignedchar flag7;
    unsignedchar flag8;  // 总共8字节!
};

内存高效的写法

struct flags_smart {
    unsignedchar flag1:1;  // 只用1位
    unsignedchar flag2:1;
    unsignedchar flag3:1;
    unsignedchar flag4:1;
    unsignedchar flag5:1;
    unsignedchar flag6:1;
    unsignedchar flag7:1;
    unsignedchar flag8:1;  // 总共1字节!
} flags;

内存节省:从8字节降到1字节!

位运算:替代乘除法

慢速版本

uint32_t val = 1024;
uint32_t doubled = val * 2;    // 乘法指令
uint32_t halved = val / 2;     // 除法指令

快速版本

uint32_t val = 1024;
uint32_t doubled = val << 1;   // 左移1位 = 乘以2
uint32_t halved = val >> 1;    // 右移1位 = 除以2

6. 循环展开 - 减少跳转

传统循环的开销

// 每次循环都有跳转开销
for (int i = 0; i < 4; i++) {
    process(array[i]);
}

展开后的高效版本

// 直接执行,无跳转开销
process(array[0]);
process(array[1]);
process(array[2]);
process(array[3]);

高级展开:并行计算

普通版本

long calc_sum_slow(int *arr0, int *arr1) {
    long sum = 0;
    for (int i = 0; i < 1000; i++) {
        sum += arr0[i] * arr1[i];  // 串行计算
    }
    return sum;
}

优化版本

long calc_sum_fast(int *arr0, int *arr1) {
    long sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
    
    for (int i = 0; i < 250; i += 4) {
        sum0 += arr0[i+0] * arr1[i+0];  // 并行计算
        sum1 += arr0[i+1] * arr1[i+1];
        sum2 += arr0[i+2] * arr1[i+2];
        sum3 += arr0[i+3] * arr1[i+3];
    }
    
    return sum0 + sum1 + sum2 + sum3;
}

7. 内联函数 - 消除函数调用开销

函数调用的隐藏成本

每次函数调用都有开销:

    参数压栈跳转指令栈帧管理返回地址保存

内联函数的魔力

static inline void toggle_led(uint8_t pin) {
    PORT ^= 1 << pin;
}

// 编译后直接展开,无函数调用开销
toggle_led(LED_PIN);

适用场景

    频繁调用的小函数关键路径上的函数简单的工具函数

8. 数据类型优化

循环变量的学问

低效写法

char i;  // 可能溢出,编译器需要额外检查
for (i = 0; i < N; i++) {
    // ...
}

高效写法

int i;   // 编译器优化更好
for (i = 0; i < N; i++) {
    // ...
}

数据类型选择原则

循环索引:优先使用int

存储优化:能用char就不用int

计算密集:避免不必要的浮点运算

类型转换:减少隐式类型转换

9. 循环优化策略

多重循环的排列艺术

低效排列(长循环在外层):

for (row = 0; row < 100; row++) {      // 外层100次
    for (col = 0; col < 5; col++) {    // 内层5次
        sum += a[row][col];
    }
}
// 总跳转次数:100次外层跳转

高效排列(长循环在内层):

for (col = 0; col < 5; col++) {       // 外层5次
    for (row = 0; row < 100; row++) {  // 内层100次
        sum += a[row][col];
    }
}
// 总跳转次数:5次外层跳转

提前退出策略

低效方法(执行完整循环):

bool found = false;
for (int i = 0; i < 10000; i++) {
    if (list[i] == target) {
        found = true;  // 找到了还继续循环!
    }
}

高效方法(找到即退出):

bool found = false;
for (int i = 0; i < 10000; i++) {
    if (list[i] == target) {
        found = true;
        break;  // 立即退出
    }
}

10. 结构体内存对齐优化

内存对齐的影响

未优化版本

struct waste_memory {
    char a;      // 1字节
    short b;     // 2字节,需要对齐
    char c;      // 1字节  
    int d;       // 4字节,需要对齐
    char e;      // 1字节
};
// 实际占用:16字节

优化版本

struct save_memory {
    char a;      // 1字节
    char c;      // 1字节
    short b;     // 2字节,正好对齐
    int d;       // 4字节,正好对齐
    char e;      // 1字节
};
// 实际占用:12字节,节省25%!

注意事项

优化原则

不要为了优化而优化;不要牺牲代码可读性。

先测量,后优化:用工具找到真正的瓶颈

权衡取舍:性能 vs 可读性 vs 维护性

渐进优化:先优化热点代码

验证结果:优化后要测试正确性

好的优化不是炫技,而是在约束条件下找到最佳平衡点。

你在嵌入式开发中还用过哪些优化技巧?

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!