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

C语言中使用函数式宏测量函数调用的执行时间

C语言中用clock()测量函数执行时间的实现与优化问题

在C语言中,标准库函数clock()是测量函数执行时间的常用方法之一。以下是对比两个不同原型函数执行时间的基础实现:

基础实现示例

#include <stdio.h>
#include <time.h>

int f(int n) { return n < 3 ? 1 : f(n-1) + f(n-2); }

int g(int n, int a, int b) { return n < 3 ? b : g(n-1, b, a+b); }

int main(void) {
    double start, elapsed1, elapsed2;
    printf("n  f   g\n");
    for(int n = 30; n <= 40; n++) {
        start = (double) clock();
        f(n); 
        elapsed1 = (clock() - start) / CLOCKS_PER_SEC;
        start = (double) clock();
        g(n, 1, 1);
        elapsed2 = ((double) clock() - start) / CLOCKS_PER_SEC;
        printf("%d %.1f %.1f\n", n, elapsed1, elapsed2);
    }
    return 0;
}

宏简化版本

由于C语言没有泛型函数指针,我们可以用函数式宏来简化重复的计时代码,减少冗余:

#include <stdio.h>
#include <time.h>

double START, ELAPSED;

#define CPUTIME(FCALL) (START = (double) clock(), FCALL, ELAPSED = ((double) clock() - START) / CLOCKS_PER_SEC)

int f(int n) { return n < 3 ? 1 : f(n-1) + f(n-2); }

int g(int n, int a, int b) { return n < 3 ? b : g(n-1,b,a+b); }

int main(void) {
    printf("n  f   g\n");
    for(int n = 30; n <= 40; n++) 
        printf("%d %.1f %.1f\n", n, CPUTIME(f(n)), CPUTIME(g(n, 1, 1)) );
    return 0;
}

技术问询

  1. 能否让START和ELAPSED变量在宏定义内局部化,同时仍可将该宏作为非void“函数”调用?
  2. 有没有不使用宏定义的更简洁代码实现方式?

问题解答

问题1:局部化宏内变量并保留非void调用特性

可以实现,借助GNU C扩展的复合语句(({ ... })),它允许在块内定义局部变量,并且整个复合语句的返回值是块内最后一个表达式的结果。这样既可以把计时变量局部化,又能让宏像函数一样返回时间值:

修改后的宏定义及示例代码:

#include <stdio.h>
#include <time.h>

#define CPUTIME(FCALL) ({ \
    clock_t _start = clock(); \
    FCALL; \
    (double)(clock() - _start) / CLOCKS_PER_SEC; \
})

int f(int n) { return n < 3 ? 1 : f(n-1) + f(n-2); }

int g(int n, int a, int b) { return n < 3 ? b : g(n-1, b, a+b); }

int main(void) {
    printf("n  f   g\n");
    for(int n = 30; n <= 40; n++) 
        printf("%d %.1f %.1f\n", n, CPUTIME(f(n)), CPUTIME(g(n, 1, 1)) );
    return 0;
}
  • 用下划线开头的_start作为局部变量,避免和用户代码中的变量名冲突
  • 复合语句最后一行的计算结果就是宏的返回值,完美适配printf等场景的表达式调用
  • 如果需要严格兼容C标准(不依赖GNU扩展),无法直接在宏内定义局部变量同时返回值,只能退而求其次用静态变量(但存在线程安全问题),因此推荐上述GNU扩展方案。

问题2:不使用宏的简洁实现

由于C语言缺乏泛型函数支持,不使用宏的话很难做到和宏一样的极致简洁,但可以通过封装核心计时逻辑来减少重复代码:

方案:封装计时开始/结束为inline函数

#include <stdio.h>
#include <time.h>

// 封装计时开始逻辑
inline double measure_start(void) {
    return (double)clock();
}

// 封装计时结束与计算逻辑
inline double measure_end(double start) {
    return ((double)clock() - start) / CLOCKS_PER_SEC;
}

int f(int n) { return n < 3 ? 1 : f(n-1) + f(n-2); }

int g(int n, int a, int b) { return n < 3 ? b : g(n-1, b, a+b); }

int main(void) {
    printf("n  f   g\n");
    for(int n = 30; n <= 40; n++) {
        double s1 = measure_start();
        f(n);
        double e1 = measure_end(s1);
        
        double s2 = measure_start();
        g(n,1,1);
        double e2 = measure_end(s2);
        
        printf("%d %.1f %.1f\n", n, e1, e2);
    }
    return 0;
}

这种方式把重复的时钟获取和计算逻辑封装成内联函数,代码比原始版本更整洁,且完全不依赖宏。缺点是每次计时需要手动调用两次函数,无法像宏那样用一行代码完成整个计时流程。

如果尝试用函数指针或回调来适配不同签名的函数,需要为每个函数写适配包装,反而会增加代码量,不如上述方案简洁。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:32:53