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组,分支预测压力降低;数据只需从内存加载一次即完成全部统计,减少了缓存行重复载入。对于长度较大的数组,此优化可显著减少循环开销;若数据量小于缓存行大小,收益主要体现在循环控制指令减少上。

合并前:3次遍历 循环1: 找最大 循环2: 找最小 循环3: 累加 合并后:1次遍历 单循环内完成: • 比较最大值 • 比较最小值 • 累加求和 数据缓存 载入次数 3次 → 1次

注意事项:若数据数组极大且无法整体放入缓存,合并循环仍能保证每个数据元素仅从主存加载一次;而分离循环在数据超过缓存容量时,后续循环可能因缓存失效而重新从主存读取,造成额外延迟。此优化在数据规模大于L1缓存时收益更明显。