2. 循环合并
任务目标
在嵌入式系统中,多个循环遍历同一块数据时,每次循环都会产生独立的循环控制开销和缓存访问模式。本案例将三个分别计算最大值、最小值和总和的独立循环合并为单个循环,在保持功能不变的前提下减少循环开销与缓存未命中。
实现代码
#include <stdint.h>
#include <stddef.h>
// 合并前:三个独立循环
void process_separate(const int16_t *data, size_t len,
int16_t *max_out, int16_t *min_out, int32_t *sum_out) {
int16_t max_val = data[0];
int16_t min_val = data[0];
int32_t sum = 0;
for (size_t i = 0; i < len; i++) { // 循环1:找最大值
if (data[i] > max_val) max_val = data[i];
}
for (size_t i = 0; i < len; i++) { // 循环2:找最小值
if (data[i] < min_val) min_val = data[i];
}
for (size_t i = 0; i < len; i++) { // 循环3:累加
sum += data[i];
}
*max_out = max_val;
*min_out = min_val;
*sum_out = sum;
}
// 合并后:单次遍历完成全部统计
void process_merged(const int16_t *data, size_t len,
int16_t *max_out, int16_t *min_out, int32_t *sum_out) {
int16_t max_val = data[0];
int16_t min_val = data[0];
int32_t sum = 0;
for (size_t i = 0; i < len; i++) {
int16_t v = data[i];
if (v > max_val) max_val = v;
if (v < min_val) min_val = v;
sum += v;
}
*max_out = max_val;
*min_out = min_val;
*sum_out = sum;
}
运行与验证
测试环境:任何C99编译器(如arm-none-eabi-gcc、gcc),无需额外依赖。
// 验证代码(main.c)
#include <stdio.h>
#include <assert.h>
int main(void) {
int16_t data[] = {3, -5, 8, 2, -9, 7, 0, 4};
size_t len = sizeof(data) / sizeof(data[0]);
int16_t mx1, mn1, mx2, mn2;
int32_t sum1, sum2;
process_separate(data, len, &mx1, &mn1, &sum1);
process_merged(data, len, &mx2, &mn2, &sum2);
// 功能一致性断言
assert(mx1 == mx2 && mx2 == 8);
assert(mn1 == mn2 && mn2 == -9);
assert(sum1 == sum2 && sum2 == 10);
printf("验证通过: max=%d, min=%d, sum=%ld\n", mx2, mn2, (long)sum2);
return 0;
}
编译运行:
gcc -O2 -Wall -o test main.c 你的代码文件名.c
./test
通过条件:程序输出验证通过: max=8, min=-9, sum=10且无assert失败。该结果由给定输入直接推出,用于确认合并前后功能完全一致。
优化效果说明
合并后循环控制指令(计数器递增、条件跳转)从3组降为1组,分支预测压力降低;数据只需从内存加载一次即完成全部统计,减少了缓存行重复载入。对于长度较大的数组,此优化可显著减少循环开销;若数据量小于缓存行大小,收益主要体现在循环控制指令减少上。
注意事项:若数据数组极大且无法整体放入缓存,合并循环仍能保证每个数据元素仅从主存加载一次;而分离循环在数据超过缓存容量时,后续循环可能因缓存失效而重新从主存读取,造成额外延迟。此优化在数据规模大于L1缓存时收益更明显。