数组遍历求值,行遍历和列遍历谁更快

例子1,判断如下分配内存的性能

c 复制代码
// 在循环内分配
for (int i = 0; i < n; i++) {
char *buf = malloc(1024);
// 使用buf
free(buf);
}
// 一次性分配
char buf = malloc(1024 * n);
for (int i = 0; i < n; i++) {
// 使用buf 
}
free(buf);
结论
  • 一次性分配(第二种)明显更快
代码验证(加入了calloc)
c 复制代码
代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define N 10000
#define BUF_SIZE 1024
// 方法1:循环内分配
void method1() {
clock_t start = clock();
for (int i = 0; i < N; i++) {
    char *buf = malloc(BUF_SIZE);
    if (!buf) {
        printf("内存分配失败!\n");
        return;
    }
    free(buf);
}
clock_t end = clock();
printf("循环内分配: %.6f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);

}
// 方法2:一次性分配
void method2() {
clock_t start = clock();
// 一次性分配所有内存
char *buf = malloc(BUF_SIZE * N);
if (!buf) {
    printf("内存分配失败!\n");
    return;
}
free(buf);

clock_t end = clock();
printf("一次性分配: %.6f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);

}
// 方法3:使用calloc
void method3() {
clock_t start = clock();
char *buf = calloc(N, BUF_SIZE);
if (!buf) {
    printf("内存分配失败!\n");
    return;
}
free(buf);

clock_t end = clock();
printf("calloc分配: %.6f 秒\n", (double)(end - start) / CLOCKS_PER_SEC);

}
int main() {
printf("测试 %d 次分配,每次 %d 字节\n\n", N, BUF_SIZE);
method1();
method2();    
method3();
return 0;

}

验证结论

循环分配和一次性分配时间差值达到了数倍,并且会根据申请内存的大小线性变化

分析
  • 循环分配
    每次分配新的内存,需要遍历内存空闲链表,开销时间较大,并且分配新的内存与之前分配的内存区域不连续,很分散,内存碎片率高,访问时,会有更多的随机访问,对缓存不友好
  • 一次性分配
    每次分配新的内存,是一次性分配,减少了内存碎片化,分配的是连续区域,集中,对缓存友好,也有更快地访问速度

例子2:一个充满小于100值的二维数组,现在要求所有元素之和,行遍历求和快还是列求和快?

结论:行求和更快

测试代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define ROWS 5000
#define COLS 5000
// 初始化数组
void init_array(int arr[ROWS][COLS]) {
srand(42);
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
arr[i][j] = rand() % 100; // 生成0-99的随机数
}
}
}
// 行求和
long long sum_by_rows(int arr[ROWS][COLS]) {
long long total = 0;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
total += arr[i][j];
}
}
return total;
}
// 列求和
long long sum_by_cols(int arr[ROWS][COLS]) {
long long total = 0;
for (int j = 0; j < COLS; j++) {
for (int i = 0; i < ROWS; i++) {
total += arr[i][j];
}
}
return total;
}
// 验证所有方法结果是否一致
int verify_results(long long results[], int count, const char* names[]) {
for (int i = 1; i < count; i++) {
if (results[i] != results[0]) {
printf("错误: %s 的结果不一致!\n", names[i]);
printf("  %s: %lld\n", names[0], results[0]);
printf("  %s: %lld\n", names[i], results[i]);
return 0;
}
}
return 1;
}
// 性能测试函数
void benchmark(const char* name, long long (func)(int[ROWS][COLS]), int arr[ROWS][COLS], long long result) {
clock_t start, end;
double cpu_time_used;
start = clock();
*result = func(arr);
end = clock();

cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("%-10s 耗时: %.4f 秒, 结果: %lld\n", name, cpu_time_used, *result);

}
int main() {
// 分配数组(使用动态分配避免栈溢出)
printf("分配 %d x %d 数组 (约 %.2f MB)...\n",
ROWS, COLS, (double)ROWS * COLS * sizeof(int) / (1024 * 1024));
int (*arr)[COLS] = malloc(ROWS * sizeof(*arr));
if (!arr) {
    printf("内存分配失败!\n");
    return 1;
}

printf("初始化数组...\n");
init_array(arr);

printf("\n开始性能测试:\n");
printf("=============================================\n");

// 存储各种方法的结果
long long results[2];
const char* method_names[2] = {
    "行求和(基础)",
    "列求和(基础)", 
};

// 执行基准测试
benchmark(method_names[0], sum_by_rows, arr, &results[0]);
benchmark(method_names[1], sum_by_cols, arr, &results[1]);

// 验证结果一致性
printf("\n验证结果一致性...\n");
if (verify_results(results, 2, method_names)) {
    printf("✓ 所有方法结果一致\n");
}
printf("=============================================\n");

// 释放内存
free(arr);
printf("\n测试完成!\n");
return 0;
}
测试结果
核心
  • 核心也是对内存的访问速度对比

对于行遍历求和的优化

  1. 优化版本1:行求和,使用局部变量减少内存访问
  2. 优化版本2:行求和,循环展开
  3. 优化版本3:分块求和(更好利用缓存)

结果验证

c 复制代码
整体代码:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define ROWS 5000
#define COLS 5000
// 初始化数组
void init_array(int arr[ROWS][COLS]) {
srand(42);
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
arr[i][j] = rand() % 100; // 生成0-99的随机数
}
}
}
// 行求和
long long sum_by_rows(int arr[ROWS][COLS]) {
long long total = 0;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
total += arr[i][j];
}
}
return total;
}
// 列求和
long long sum_by_cols(int arr[ROWS][COLS]) {
long long total = 0;
for (int j = 0; j < COLS; j++) {
for (int i = 0; i < ROWS; i++) {
total += arr[i][j];
}
}
return total;
}
// 优化版本1:行求和,使用局部变量减少内存访问
long long sum_by_rows_optimized(int arr[ROWS][COLS]) {
long long total = 0;
for (int i = 0; i < ROWS; i++) {
long long row_sum = 0; // 使用局部变量
for (int j = 0; j < COLS; j++) {
row_sum += arr[i][j];
}
total += row_sum;
}
return total;
}
// 优化版本2:行求和,循环展开
long long sum_by_rows_unrolled(int arr[ROWS][COLS]) {
long long total = 0;
const int UNROLL = 4;
for (int i = 0; i < ROWS; i++) {
long long row_sum = 0;
int j = 0;
// 手动循环展开
for (; j + UNROLL <= COLS; j += UNROLL) {
row_sum += arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i][j+3];
}
// 处理剩余元素
for (; j < COLS; j++) {
row_sum += arr[i][j];
}
total += row_sum;
}
return total;
}
// 优化版本3:分块求和(更好利用缓存)
long long sum_by_rows_blocked(int arr[ROWS][COLS]) {
long long total = 0;
const int BLOCK_SIZE = 256; // 选择适合缓存大小的块
for (int bi = 0; bi < ROWS; bi += BLOCK_SIZE) {
    for (int i = bi; i < bi + BLOCK_SIZE && i < ROWS; i++) {
        long long row_sum = 0;
        for (int j = 0; j < COLS; j++) {
            row_sum += arr[i][j];
        }
        total += row_sum;
    }
}
return total;

}
// 验证所有方法结果是否一致
int verify_results(long long results[], int count, const char* names[]) {
for (int i = 1; i < count; i++) {
if (results[i] != results[0]) {
printf("错误: %s 的结果不一致!\n", names[i]);
printf("  %s: %lld\n", names[0], results[0]);
printf("  %s: %lld\n", names[i], results[i]);
return 0;
}
}
return 1;
}
// 性能测试函数
void benchmark(const char* name, long long (func)(int[ROWS][COLS]), int arr[ROWS][COLS], long long result) {
clock_t start, end;
double cpu_time_used;
start = clock();
*result = func(arr);
end = clock();

cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("%-10s 耗时: %.4f 秒, 结果: %lld\n", name, cpu_time_used, *result);

}
int main() {
// 分配数组(使用动态分配避免栈溢出)
printf("分配 %d x %d 数组 (约 %.2f MB)...\n",
ROWS, COLS, (double)ROWS * COLS * sizeof(int) / (1024 * 1024));
int (*arr)[COLS] = malloc(ROWS * sizeof(*arr));
if (!arr) {
    printf("内存分配失败!\n");
    return 1;
}

printf("初始化数组...\n");
init_array(arr);

printf("\n开始性能测试:\n");
printf("=============================================\n");

// 存储各种方法的结果
long long results[5];
const char* method_names[5] = {
    "行求和(基础)",
    "列求和(基础)", 
    "行求和(优化)",
    "行求和(循环展开)",
    "行求和(分块)"
};

// 执行基准测试
benchmark(method_names[0], sum_by_rows, arr, &results[0]);
benchmark(method_names[1], sum_by_cols, arr, &results[1]);
benchmark(method_names[2], sum_by_rows_optimized, arr, &results[2]);
benchmark(method_names[3], sum_by_rows_unrolled, arr, &results[3]);
benchmark(method_names[4], sum_by_rows_blocked, arr, &results[4]);

// 验证结果一致性
printf("\n验证结果一致性...\n");
if (verify_results(results, 5, method_names)) {
    printf("✓ 所有方法结果一致\n");
}
printf("=============================================\n");
// 释放内存
free(arr);
printf("\n测试完成!\n");
return 0;

}

优化结果

  • 当前结果时间从小到大排序:
    行求和(循环展开)<行求和(基础)<行求和(分块)<行求和(优化)<列求和(基础)
相关推荐
qingyun9892 小时前
深度优先遍历:JavaScript递归查找树形数据结构中的节点标签
前端·javascript·数据结构
胡楚昊2 小时前
NSSCTF动调题包通关
开发语言·javascript·算法
Gold_Dino3 小时前
agc011_e 题解
算法
bubiyoushang8883 小时前
基于蚁群算法的直流电机PID参数整定 MATLAB 实现
数据结构·算法·matlab
风筝在晴天搁浅3 小时前
hot100 240.搜索二维矩阵Ⅱ
算法·矩阵
南棱笑笑生3 小时前
20251224给飞凌OK3588-C开发板适配Rockchip原厂的Buildroot【linux-6.1】系统时确认ssh服务【内置dropbear】
linux·c语言·ssh·rockchip
girl-07263 小时前
2025.12.24代码分析
算法
永远睡不够的入4 小时前
直接插入排序、希尔排序、选择排序
数据结构·算法·排序算法
历程里程碑4 小时前
hot 206
java·开发语言·数据结构·c++·python·算法·排序算法