Linux C语言计时有哪些常用方法?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

在 Linux C 语言编程中,计时是一个常见的需求,主要用于性能分析、测试代码执行时间等,Linux 提供了多种计时方法,主要可以分为两大类:

  1. 高精度计时:使用 clock_gettime(),这是目前推荐的方法,精度可达纳秒级,且不受进程调度影响。
  2. 传统计时:使用 clock() 函数,精度较低(通常为毫秒级),且受系统负载影响。

下面我将详细介绍这两种方法,并提供完整的代码示例。


高精度计时:clock_gettime()

这是现代 Linux 系统中最常用、最可靠的计时方法,它属于 POSIX 标准,可以获取高精度的时间戳。

函数原型

#include <time.h>
int clock_gettime(clockid_t clk_id, struct timespec *tp);

参数说明

  • clk_id: 指定要获取哪种类型的时间,常用的有:
    • CLOCK_REALTIME: 系统的实时时钟,与系统时间一致,可以被 settime 修改,适合测量绝对时间。
    • CLOCK_MONOTONIC: 单调递增的时钟,不受系统时间修改的影响,非常适合用于测量一个事件的持续时间,因为它只会向前走。
    • CLOCK_PROCESS_CPUTIME_ID: 当前进程在用户态和内核态消耗的 CPU 时间。
    • CLOCK_THREAD_CPUTIME_ID: 当前线程在用户态和内核态消耗的 CPU 时间。
  • tp: 一个指向 struct timespec 结构体的指针,用于存储获取到的时间。

struct timespec 结构体

struct timespec {
    time_t tv_sec;    // 秒 (seconds)
    long   tv_nsec;   // 纳秒 (nanoseconds)
};

计算时间差

为了计算一个代码块的执行时间,你需要分别在代码块前后调用 clock_gettime(),然后计算两个时间戳的差值。

差值计算函数:

#include <math.h>
// 计算两个 timespec 结构的差值,结果单位为秒
double diff_timespec(const struct timespec *start, const struct timespec *end) {
    double diff_sec = end->tv_sec - start->tv_sec;
    double diff_nsec = end->tv_nsec - start->tv_nsec;
    return diff_sec + diff_nsec / 1000000000.0;
}

完整示例

这个例子演示如何使用 CLOCK_MONOTONIC 来测量一个循环的执行时间。

#include <stdio.h>
#include <time.h>
#include <math.h> // 用于 floor 函数
// 计算时间差函数
double diff_timespec(const struct timespec *start, const struct timespec *end) {
    double diff_sec = end->tv_sec - start->tv_sec;
    double diff_nsec = end->tv_nsec - start->tv_nsec;
    return diff_sec + diff_nsec / 1000000000.0;
}
int main() {
    struct timespec start, end;
    double elapsed_time;
    // 获取开始时间
    // 使用 CLOCK_MONOTONIC 确保时间单调递增,不受系统时间影响
    if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) {
        perror("clock_gettime");
        return 1;
    }
    // --- 要计时的代码块 ---
    printf("开始执行一个耗时任务...\n");
    for (volatile int i = 0; i < 100000000; ++i) {
        // 一个空循环,模拟耗时操作
        // volatile 关键字防止编译器优化掉这个循环
    }
    printf("任务执行完毕,\n");
    // --- 计时代码块结束 ---
    // 获取结束时间
    if (clock_gettime(CLOCK_MONOTONIC, &end) == -1) {
        perror("clock_gettime");
        return 1;
    }
    // 计算并打印耗时
    elapsed_time = diff_timespec(&start, &end);
    printf("使用 clock_gettime(CLOCK_MONOTONIC) 计时:\n");
    printf("执行时间: %.9f 秒\n", elapsed_time);
    printf("执行时间: %.3f 毫秒\n", elapsed_time * 1000);
    printf("执行时间: %.0f 微秒\n", elapsed_time * 1000000);
    return 0;
}

编译与运行:

gcc -o timing timing.c -lm
./timing

输出示例:

开始执行一个耗时任务...
任务执行完毕。
使用 clock_gettime(CLOCK_MONOTONIC) 计时:
执行时间: 0.045123456 秒
执行时间: 45.123 毫秒
执行时间: 45123 微秒

传统计时:clock()

clock() 是 C 标准库 <time.h> 中的函数,非常古老和普遍,但在现代高精度应用中已不推荐。

函数原型

#include <time.h>
clock_t clock(void);

返回值

  • 返回自程序启动以来,处理器消耗的时间,单位是“时钟滴答数”(clock ticks)。
  • 要将其转换为秒,需要除以 CLOCKS_PER_SEC 宏,这个宏表示每秒有多少个时钟滴答。

特点

  • 精度较低:精度取决于 CLOCKS_PER_SEC 的值,通常是毫秒级(1000 Hz)。
  • 受系统负载影响:它测量的是 CPU 时间,而不是挂钟时间,如果你的进程因为等待 I/O 或其他原因被阻塞,clock() 返回的时间不会增加,这对于测量程序响应时间是不准确的。
  • 可能溢出clock_t 通常是 longlong long 类型,对于长时间运行的程序,可能会溢出。

完整示例

#include <stdio.h>
#include <time.h>
int main() {
    clock_t start, end;
    double cpu_time_used;
    // 获取开始时间
    start = clock();
    // --- 要计时的代码块 ---
    printf("开始执行一个耗时任务...\n");
    for (volatile int i = 0; i < 100000000; ++i) {
        // 空循环
    }
    printf("任务执行完毕,\n");
    // --- 计时代码块结束 ---
    // 获取结束时间
    end = clock();
    // 计算耗时 (end - start) 得到滴答数,再除以每秒滴答数
    cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    printf("使用 clock() 计时:\n");
    printf("CPU 执行时间: %f 秒\n", cpu_time_used);
    return 0;
}

编译与运行:

gcc -o timing_legacy timing_legacy.c
./timing_legacy

输出示例:

开始执行一个耗时任务...
任务执行完毕。
使用 clock() 计时:
CPU 执行时间: 0.045123 秒

其他计时方法

gettimeofday()

这是一个过时的、非标准的系统调用,但在很多旧代码中仍然可见,它提供微秒级精度。

#include <sys/time.h>
struct timeval {
    time_t      tv_sec;     // 秒
    suseconds_t tv_usec;    // 微秒
};

为什么不推荐?

  • 不是 POSIX 标准,是 BSD 特有的。
  • 精度(微秒)低于 clock_gettime()(纳秒)。
  • struct timeval 使用 suseconds_t (通常是 32 位),在约 70 分钟后会发生溢出,而 struct timespec 使用 long (64位),几乎不会溢出。

time()difftime()

这两个函数仅用于获取日历时间,精度为秒,完全不适用于性能测量。

#include <time.h>
time_t time(time_t *t);
double difftime(time_t time1, time_t time0);

总结与选择

方法 精度 推荐用途 优点 缺点
clock_gettime() 纳秒 性能分析、测量耗时 高精度、标准 POSIX、单调可靠、不易溢出 需要链接 -lrt (在极少数旧系统上)
clock() 毫秒 快速原型、旧代码兼容 C标准库,无需额外链接 精度低、测量CPU时间而非挂钟时间、可能溢出
gettimeofday() 微秒 不推荐的新项目 微秒级精度 非标准、可能溢出、精度低于 clock_gettime
time() 获取当前日期/时间 简单 精度太低,无法用于计时

在 Linux C 语言中进行计时,强烈推荐使用 clock_gettime(),它提供了最高的精度、最好的可靠性和最标准的接口。

  • 如果你想测量一个程序或代码块的总耗时(包括阻塞时间),请使用 CLOCK_MONOTONIC
  • 如果你想测量一个进程或线程的 CPU 占用时间,请使用 CLOCK_PROCESS_CPUTIME_IDCLOCK_THREAD_CPUTIME_ID
-- 展开阅读全文 --
头像
dede留言加验证码
« 上一篇 前天
织梦 顶部登录 代码
下一篇 » 前天

相关文章

取消
微信二维码
支付宝二维码

目录[+]