初始化与未初始化值性能测试的有效性及结果可信度问询
初始化与未初始化内存的性能测试疑问与分析
测试背景
我看到不同视频对初始化值与未初始化值的性能差异说法完全相反,于是自己编写了C语言测试代码验证。
测试代码
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <sys/time.h> void a(int size) { int* a = malloc(sizeof(int) * size); memset(a, 0, sizeof(int) * size); for(int i = 0; i < size; i++) { a[i] = i; } free(a); } void b(int size) { int* a = malloc(sizeof(int) * size); for(int i = 0; i < size; i++) { a[i] = i; } free(a); } int test(void fun(int), int runs, int size) { int time = 0; for(int i = 0; i < runs; i++) { struct timeval beg = {0}, end = {0}; gettimeofday(&beg, NULL); fun(size); gettimeofday(&end, NULL); int usec_diff = end.tv_usec - beg.tv_usec; if(usec_diff < 0) { time += 1000000; } else { time += usec_diff; } } return time; } int main() { int a_3 = test(&a, 100000, 1000); int b_3 = test(&b, 100000, 1000); printf("b/a ^3: %f\n", (float)b_3 / a_3); int a_4 = test(&a, 100000, 10000); int b_4 = test(&b, 100000, 10000); printf("b/a ^4: %f\n", (float)b_4 / a_4); int a_5 = test(&a, 100000, 100000); int b_5 = test(&b, 100000, 100000); printf("b/a ^5: %f\n", (float)b_5 / a_5); }
测试结果
eck@eck:~/src/test$ ./main b/a ^3: 2.764046 b/a ^4: 0.989866 b/a ^5: 0.990233
核心疑问
- 首次编写这类性能测试,想确认测试是否合理、结果是否可信
- 开启-O1优化后,两个函数生成的汇编代码完全相同,想了解复杂场景下的情况,以及初始化值与未初始化值的性能差异究竟如何
测试合理性与结果可信度分析
测试的合理性
- 你的测试框架具备基础计时逻辑,但存在几个可优化细节:
- 计时逻辑有漏洞:只计算了
tv_usec的差值,未处理tv_sec的跨秒情况,正确耗时计算应为(end.tv_sec - beg.tv_sec)*1000000 + (end.tv_usec - beg.tv_usec) - 缺少预热步骤:循环测试前应先调用几次测试函数,让CPU缓存、内存页表进入稳定状态,避免前几次调用的干扰
- 测试参数可调整:小内存(1000个int,4KB)跑100000次易被操作系统内存缓存过度优化,大内存测试次数可适当减少以缩短测试时间
- 计时逻辑有漏洞:只计算了
- 结果趋势可信:小内存下未初始化版本耗时更高,大内存下两者性能接近,符合操作系统内存管理的实际机制
- 你的测试框架具备基础计时逻辑,但存在几个可优化细节:
-O1优化后汇编相同的原因
- 编译器会执行冗余操作消除:你的代码中
memset初始化的内存被后续for循环完全覆盖,编译器能识别出memset是无用操作,直接将其删除,因此两个函数的汇编代码完全一致 - 这种优化仅在“初始化操作被后续写操作完全覆盖”的场景生效,如果初始化后有读操作、或后续写操作未覆盖全部初始化区域,编译器不会消除
memset
- 编译器会执行冗余操作消除:你的代码中
初始化与未初始化内存的性能差异本质
小内存场景(如4KB)
- 操作系统内存页通常为4KB,小内存刚好占一页。
malloc返回的未初始化内存采用懒加载机制:只有第一次写入时才会触发缺页中断分配物理页 - 而
memset会提前触发缺页中断完成物理页分配,后续for循环写入时无需再触发中断。因此小内存下未初始化版本因额外的缺页中断,总耗时更高,对应你看到的b/a≈2.7
- 操作系统内存页通常为4KB,小内存刚好占一页。
大内存场景(如400KB、4MB)
- 内存超过一定大小后,无论是否初始化,都会触发大量缺页中断。此时
memset的开销相对于缺页中断可忽略,或操作系统的预分配机制会抹平两者差异,因此b/a接近1
- 内存超过一定大小后,无论是否初始化,都会触发大量缺页中断。此时
复杂场景下的差异
- 若初始化后存在读操作:未初始化内存可能因读取随机值导致分支预测失败,或因未分配物理页触发缺页中断,性能更差
- 若内存多次复用(如
free后再次malloc):未初始化内存可能是已分配的热页,此时初始化的开销会比首次malloc时更明显 - 实时或延迟敏感场景:未初始化内存的缺页中断会带来不可预测的延迟,而初始化可提前加载内存页,保证延迟稳定
内容的提问来源于stack exchange,提问作者ashton
相关产品推荐
相关产品推荐

