Windows平台下C语言高精度代码耗时对比及低重复次数精度优化咨询
嘿,老兄,完全懂你这种纠结——想用计时工具测代码速度,结果单次跑的波动大到离谱,甚至差10倍,要是遇到单次跑就要好几秒的代码,跑十万次简直要等到天荒地老!我给你几个简单易实现的法子,既能提升精度,又能大幅减少重复次数:
1. 拉大单次测量的时间窗口,降低相对误差
你现在每次只测N=10000次rand循环,耗时可能只有微秒级,而gettimeofday本身的精度(加上系统调度的噪声)就占了很大比例,波动自然大。咱们可以把内部循环重复多轮,打包成一次测量,再除以轮数得到单次的时间。比如把内部循环跑K=100次,这样单次测量的时间就从微秒级升到毫秒级,噪声的影响就被稀释了,重复次数M就能从10万直接降到100左右,结果还更稳定。
2. 跳过第一次运行的「热身」开销
程序第一次执行目标代码时,会有内存分配、缓存加载、动态链接库初始化这些额外开销,这部分时间根本不是你要测的代码耗时。把第一次的测量结果直接扔掉,只统计后面的正式测试数据,能去掉一大波异常值。
3. 别只看平均值,用中位数/百分位数更靠谱
平均值很容易被极端值(比如系统突然跳出来个后台任务抢资源,导致某次耗时暴增)带偏,中位数能反映最典型的性能表现,95%/99%百分位数还能帮你看到最坏情况的波动。这几个统计量一起看,比单独的平均值靠谱多了。
4. 给测试「降噪」:控制运行环境
- 测试时关掉所有后台无关程序(浏览器、聊天软件、杀毒软件自动扫描啥的),避免系统资源被抢占;
- 如果是Linux环境(你用了
gettimeofday,Windows下建议换QueryPerformanceCounter,后面说),用taskset -c 0 ./你的程序把进程绑到单个CPU核心,避免线程切换带来的缓存失效和调度波动; - 尽量在系统 idle 的时候测试,别在编译大项目或者下载东西的时候跑。
针对你示例代码的修改版本
我把上面的思路整合到你的代码里了,你可以直接跑试试:
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <assert.h> #include <sys/time.h> #include <time.h> // 用于qsort的比较函数,计算中位数用 int compare_doubles(const void *a, const void *b) { const double *da = (const double *)a; const double *db = (const double *)b; return (*da > *db) - (*da < *db); } double *Vec(int n) { double *p = (double*)malloc(n * sizeof(double)); assert(p != NULL); return p; } int main() { int N = 10000; int K = 100; // 内部循环重复K次,拉大单次测量窗口 int M = 100; // 重复次数从10万降到100,节省时间 double* v = Vec(N); double* t = Vec(M); struct timeval begin, end; srand(time(0)); // 热身:第一次运行,扔掉结果 gettimeofday(&begin, 0); for (int k=0; k<K; k++) { for (int i=0; i<N; i++) { v[i] = rand() % 100; } } gettimeofday(&end, 0); // 正式测量 for (int j=0; j<M; j++) { gettimeofday(&begin, 0); for (int k=0; k<K; k++) { for (int i=0; i<N; i++) { v[i] = rand() % 100; //v[i] = rand()*100/(RAND_MAX +1.0); } } gettimeofday(&end, 0); long seconds = end.tv_sec - begin.tv_sec; long microseconds = end.tv_usec - begin.tv_usec; t[j] = (seconds + microseconds*1e-6) / K; // 除以K得到单次N循环的时间 } // 计算平均值和中位数 double tmean = 0; for (int j=0; j<M; j++) { tmean += t[j]/M; } qsort(t, M, sizeof(double), compare_doubles); double tmedian = t[M/2]; printf("Mean time: %.6f ms.\n", tmean*1000); printf("Median time: %.6f ms.\n", tmedian*1000); free(v); free(t); return 0; }
补充:Windows平台的高精度计时替换
你标题提到Windows平台,但gettimeofday不是Windows标准API,Windows下建议用QueryPerformanceCounter,精度比gettimeofday更高,替换起来也简单:
// 替换原来的gettimeofday测量部分 #include <windows.h> LARGE_INTEGER freq, start, end; QueryPerformanceFrequency(&freq); // 获取计数器频率 QueryPerformanceCounter(&start); // 你的目标代码块 for (int k=0; k<K; k++) { for (int i=0; i<N; i++) { v[i] = rand() % 100; } } QueryPerformanceCounter(&end); double elapsed = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart; t[j] = elapsed / K;
总结一下:核心就是让单次测量的时间足够长,把噪声的影响降到最低,再配合鲁棒的统计指标和干净的测试环境,不需要成千上万次重复就能得到可靠的性能对比结果。
备注:内容来源于stack exchange,提问作者D. Alfano




