You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中测量mmap()这类快速函数的用户时间与系统时间

问题:精准测量mmap()的用户/系统时间,排除无关操作干扰

我希望只统计mmap()函数本身的用户时间与系统时间,排除错误检查、munmap()以及循环逻辑的耗时影响。目前我的初始实现如下:

void measure_mmap_time(double *user_time, double *system_time) {
    struct rusage start, end;
    int pagesize = sysconf(_SC_PAGESIZE); 
    double total_user_time = 0;
    double total_system_time = 0;

    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        getrusage(RUSAGE_SELF, &start); 
        // Map a page of memory
        void *addr = mmap(NULL, pagesize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
        getrusage(RUSAGE_SELF, &end); 
        
        if (addr == MAP_FAILED) {
            perror("mmap");
            exit(EXIT_FAILURE);
        }
        
        if (munmap(addr, pagesize) == -1) {
            perror("munmap");
            exit(EXIT_FAILURE);
        }
       
        // Calculate user and system time for this iteration
        total_user_time += get_time_diff(start.ru_utime, end.ru_utime);
        total_system_time += get_time_diff(start.ru_stime, end.ru_stime);
    }

    *user_time = total_user_time / NUM_ITERATIONS;
    *system_time = total_system_time / NUM_ITERATIONS;
}

但我想把getrusage(RUSAGE_SELF, &start)和getrusage(RUSAGE_SELF, &end)移到循环外,同时保留munmap()在循环内,于是编写了新的实现,试图通过空循环抵消循环本身的开销:

void measure_mmap_time(double *user_time, double *system_time) {
    struct rusage start, end;
    int pagesize = sysconf(_SC_PAGESIZE); 
    double total_user_time = 0;
    double total_system_time = 0;

    void *addresses[NUM_ITERATIONS];

    getrusage(RUSAGE_SELF, &start); 
    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        // Map a page of memory
        addresses[i] = mmap(NULL, pagesize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    }
    getrusage(RUSAGE_SELF, &end); 

    for (int i = 0; i < NUM_ITERATIONS; ++i) {
        if (munmap(addresses[i], pagesize) == -1) {
            perror("munmap");
            exit(EXIT_FAILURE);
        }
    }
       
    // Calculate user and system time for this iteration
    total_user_time = get_time_diff(start.ru_utime, end.ru_utime);
    total_system_time = get_time_diff(start.ru_stime, end.ru_stime);

    getrusage(RUSAGE_SELF, &start);
    for (int i = 0; i < NUM_ITERATIONS; ++i) {
    }
    getrusage(RUSAGE_SELF, &end);

    double total_user_time2 = get_time_diff(start.ru_utime, end.ru_utime);
    double total_system_time2 = get_time_diff(start.ru_stime, end.ru_stime);

    *user_time = (total_user_time - total_user_time2) / NUM_ITERATIONS;
    *system_time = (total_system_time - total_system_time2) / NUM_ITERATIONS;
}

请问这种新方案是否合理?


分析与结论

你的新方案核心思路是成立的,但存在几个需要修正的细节,以下是具体分析:

1. 核心思路的合理性

将批量mmap操作放在统一计时区间内,再通过空循环耗时抵消循环本身的用户态开销,这个做法能有效减少getrusage的调用次数(原方案每次循环都调用两次getrusage,本身会引入额外开销),同时避免munmap和错误检查干扰计时,整体逻辑是合理的。

2. 存在的问题与优化点

(1)空循环被编译器优化的风险

现代编译器会识别空循环并直接优化掉,导致total_user_time2和total_system_time2几乎为0,无法真实抵消循环本身的耗时。解决方法是给空循环添加一个无意义但能阻止优化的操作:

volatile int dummy = 0;
getrusage(RUSAGE_SELF, &start);
for (int i = 0; i < NUM_ITERATIONS; ++i) {
    dummy++; // 强制编译器执行循环逻辑
}
getrusage(RUSAGE_SELF, &end);

也可以在编译时添加-O0禁用优化,但这会影响整体代码执行效率,不如添加volatile变量灵活。

(2)缺失mmap的即时错误检查

新方案中mmap后没有立即判断是否映射成功,如果某次mmap返回MAP_FAILED,后续的munmap操作会传入无效地址,引发未定义行为。应该在mmap循环内就做错误检查,并释放已成功映射的内存:

for (int i = 0; i < NUM_ITERATIONS; ++i) {
    addresses[i] = mmap(NULL, pagesize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (addresses[i] == MAP_FAILED) {
        perror("mmap");
        // 清理已成功映射的内存
        for (int j = 0; j < i; j++) {
            munmap(addresses[j], pagesize);
        }
        exit(EXIT_FAILURE);
    }
}

(3)批量mmap的缓存效应

多次连续mmap会让内核的内存分配器、页表等数据结构处于"热"状态,测量出的耗时会比单次冷启动的mmap更短。如果需要测量真实场景中单次mmap的平均耗时(包括冷启动),原方案每次mmap后立即munmap的方式更接近实际情况;如果是测量稳态下的mmap耗时,新方案的批量方式更合适。

3. 两种方案的对比

  • 原方案:每次循环单独计时,能避免缓存效应,但getrusage的调用开销会被平均到每次迭代中,当NUM_ITERATIONS很大时,这个开销占比可以忽略,但迭代次数较少时影响明显。
  • 新方案:减少了getrusage的调用次数,降低了这部分开销,通过空循环抵消循环逻辑的耗时,测量精度理论上更高,但需要处理编译器优化和缓存效应的问题。

内容的提问来源于stack exchange,提问作者union77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:05:01