例子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:行求和,使用局部变量减少内存访问

- 优化版本2:行求和,循环展开

- 优化版本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;
}
优化结果

- 当前结果时间从小到大排序:
行求和(循环展开)<行求和(基础)<行求和(分块)<行求和(优化)<列求和(基础)