C语言time函数与shell时间命令有何区别?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文
特性 Shell 内置 time (Bash) 外部命令 /usr/bin/time
位置 Shell 解释器的一部分(内置) 一个独立的可执行文件
调用方式 直接在命令前加 time 使用完整路径 time 或通过 command time
输出格式 相对固定,易于阅读 高度可定制,可通过环境变量修改
额外信息 仅显示:实际时间、用户CPU时间、系统CPU时间 极其丰富:内存、I/O、上下文切换、页面错误等
精度 通常较低(秒级) 非常高(微秒级)
测量对象 只能测量单个简单命令管道的第一部分 可以测量整个管道,或任何复杂的命令组合

Shell 内置的 time (例如在 Bash 中)

当你直接在终端输入 time 时,你使用的是 Shell(如 Bash、Zsh)内置的 time 关键字,它非常方便,但功能相对有限。

c语言 time shell
(图片来源网络,侵删)

基本用法

# 测量一个简单命令的执行时间
$ time sleep 2
real    0m2.005s
user    0m0.000s
sys     0m0.002s

输出详解

  • real (实际时间 / wall-clock time): 从命令开始到结束所经过的真实世界时间,这是你从时钟上看到的时间流逝,它包括了命令执行本身、等待I/O(如读写文件、网络请求)、以及被操作系统调度其他进程所消耗的时间。
  • user (用户CPU时间): 命令在用户态下消耗的CPU时间,这是程序执行自己代码所花费的时间。
  • sys (系统CPU时间): 命令在内核态下消耗的CPU时间,这是程序请求操作系统内核服务(如文件读写、内存分配)所花费的时间。

总CPU时间 = user + sys。

对于 sleep 2 这样的命令,它大部分时间都在等待,real 时间远大于 usersys 时间之和,对于一个CPU密集型任务(如编译代码),usersys 时间之和会接近 real 时间。

局限性

  1. 功能有限:只能显示这三个基本的时间信息。
  2. 精度较低time 命令本身的输出精度通常是秒级,虽然它内部可能测量得更精确,但显示结果被截断了。
  3. 无法测量复杂管道:这是它最大的一个限制。
# 这是一个错误的用法,只会测量 ls 的执行时间
$ time ls -l | grep "my_file"
real    0m0.002s
user    0m0.001s
sys     0m0.001s
# 你看不到 grep 的执行时间

外部命令 /usr/bin/time

为了克服内置 time 的限制,Linux 和 macOS 系统都提供了一个功能更强大的外部命令,通常位于 /usr/bin/time,它的输出更详细,精度也更高。

基本用法

为了避免与 Shell 内置的 time 冲突,你需要使用以下几种方式之一来调用它:

c语言 time shell
(图片来源网络,侵删)
# 使用完整路径
$ /usr/bin/time ls
# 使用 command 关键字(推荐)
$ command time ls
# 在脚本中,通常使用绝对路径

强大的输出信息

外部 time 命令的输出非常丰富,你可以通过设置环境变量 TIMEFORMAT 来自定义输出格式,或者直接使用其默认的详细输出。

示例 1:测量一个C程序的编译

$ /usr/bin/time gcc -O2 my_program.c -o my_program
0.03user 0.01system 0:00.04elapsed 99%CPU (0avgtext+0avgdata 2328maxresident)k
0inputs+0outputs (0major+105minor)pagefaults 0swaps

输出详解(从左到右):

  • 03user: 用户态CPU时间(秒)。
  • 01system: 系统态CPU时间(秒)。
  • 0:00.04elapsed: 实际经过时间(分:秒)。
  • 99%CPU: CPU利用率。
  • (0avgtext+0avgdata 2328maxresident)k: 内存使用情况。
    • avgtext: 平均文本段大小(代码)。
    • avgdata: 平均数据段大小。
    • maxresident: 最大常驻内存集大小(KB)。
  • 0inputs+0outputs: I/O 操作统计。
  • (0major+105minor)pagefaults: 页错误统计。
    • major: 重大页错误(需要从磁盘读取)。
    • minor: 次要页错误(在内存中找到,但需要处理)。
  • 0swaps: 交换次数。

示例 2:正确测量整个管道

这是外部 time 相对于内置 time 的巨大优势。

# 这会测量 ls 和 grep 的总执行时间和资源消耗
$ /usr/bin/time ls -l | grep "my_file"

自定义输出格式

你可以通过 TIMEFORMAT 环境变量来指定你想要的输出格式,类似于 printf 的格式化字符串。

# 设置只显示实际时间,精确到小数点后3位
$ export TIMEFORMAT="%3R seconds"
$ /usr/bin/time sleep 1.234
1.234 seconds

TIMEFORMAT 中常用的格式说明符:

  • %R: 实际时间(秒),浮点数。
  • %U: 用户CPU时间(秒)。
  • %S: 系统CPU时间(秒)。
  • %P: CPU利用率(百分比)。

C 语言中的 time.h

现在我们转向 C 语言本身,C 语言通过标准库 <time.h> 提供了测量时间的函数,让你可以在程序内部进行高精度的性能分析。

主要函数和数据结构

  1. clock_t clock(void):

    • 功能: 返回自程序启动以来,处理器所消耗的时钟滴答数。
    • 头文件: <time.h>
    • 精度: 取决于 CLOCKS_PER_SEC 的值,通常是毫秒级。
    • 特点: 它测量的是CPU时间,而不是真实时间,如果一个程序大部分时间都在等待I/O,clock() 返回的时间会很少。
    #include <stdio.h>
    #include <time.h>
    int main() {
        clock_t start, end;
        double cpu_time_used;
        start = clock();
        // 要测量的代码块
        for (int i = 0; i < 1000000; i++) {
            // do something
        }
        end = clock();
        cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
        printf("CPU time used: %f seconds\n", cpu_time_used);
        return 0;
    }
  2. *`time_t time(time_t timer)`**:

    • 功能: 返回自 Epoch (1970-01-01 00:00:00 UTC) 以来的秒数,这是一个真实世界时间
    • 头文件: <time.h>
    • 精度: 秒级。
    • 用途: 主要用于获取当前时间戳,而不是测量代码执行时间。
  3. int gettimeofday(struct timeval *tv, struct timezone *tz):

    • 功能: 获取当前时间,精度为微秒(microseconds),这是测量代码执行时间的首选方法之一,因为它同时提供了秒和微秒。
    • 头文件: <sys/time.h> (非标准,但在几乎所有Unix-like系统上都可用)
    • 特点: 测量的是真实时间
    #include <stdio.h>
    #include <sys/time.h>
    int main() {
        struct timeval start, end;
        long long elapsed;
        gettimeofday(&start, NULL);
        // 要测量的代码块
        for (int i = 0; i < 1000000; i++) {
            // do something
        }
        gettimeofday(&end, NULL);
        elapsed = (end.tv_sec - start.tv_sec) * 1000000LL;
        elapsed += (end.tv_usec - start.tv_usec);
        printf("Time elapsed: %lld microseconds\n", elapsed);
        return 0;
    }
  4. *`int clock_gettime(clockid_t clk_id, struct timespec tp)`**:

    • 功能: POSIX 标准推荐的高精度时间获取函数,比 gettimeofday 更现代、更灵活。
    • 头文件: <time.h>
    • 精度: 纳秒级(实际精度取决于系统)。
    • clk_id: 可以指定时钟类型。
      • CLOCK_REALTIME: 类似于 time(),返回真实世界时间。
      • CLOCK_MONOTONIC: 推荐用于性能测量,它返回一个单调递增的时间,不受系统时间更改(如NTP同步或手动修改时间)的影响。
      • CLOCK_PROCESS_CPUTIME_ID: 测量当前进程的CPU时间,类似于 clock() 但精度更高。
    #include <stdio.h>
    #include <time.h>
    int main() {
        struct timespec start, end;
        long long elapsed;
        clock_gettime(CLOCK_MONOTONIC, &start);
        // 要测量的代码块
        for (int i = 0; i < 1000000; i++) {
            // do something
        }
        clock_gettime(CLOCK_MONOTONIC, &end);
        elapsed = (end.tv_sec - start.tv_sec) * 1000000000LL;
        elapsed += (end.tv_nsec - start.tv_nsec);
        printf("Time elapsed: %lld nanoseconds\n", elapsed);
        return 0;
    }

总结与最佳实践

场景 推荐工具 原因
快速查看一个命令的执行时间 time (Shell 内置) 方便快捷,无需记忆路径。
测量整个管道或复杂脚本 /usr/bin/timecommand time 能正确测量管道中所有命令的总耗时和资源消耗。
需要详细的性能分析(内存、I/O等) /usr/bin/time 提供最全面的系统资源使用报告。
在C程序中测量代码块的性能 clock_gettime(CLOCK_MONOTONIC, ...) 最佳实践,精度高、不受系统时间影响,是现代C/C++程序的首选。
在C程序中测量CPU时间 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) 精度比 clock() 高,且是POSIX标准,比 clock() 更可靠。
在旧代码或简单场景中测量CPU时间 clock() 简单易用,但精度和灵活性有限。
  • 在 Shell 外部测量命令:用 /usr/bin/time
  • 在 C 程序内部测量代码:用 clock_gettime
-- 展开阅读全文 --
头像
织梦图片瀑布流模板如何实现自适应布局?
« 上一篇 昨天
织梦畅言评论模块怎么用?
下一篇 » 昨天

相关文章

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

目录[+]