time(1)输出中'real'、'user'和'sys'的含义

time(1)输出中’real’、’user’和’sys’的含义

技术背景

在使用time命令分析程序性能时,会看到realusersys这三个时间统计信息。理解它们的含义有助于我们深入了解程序的运行状况,判断程序是CPU密集型还是I/O密集型等。

实现步骤

理解三个时间的含义

  • Real:指实际经过的时间,即从调用开始到结束的挂钟时间。这包括其他进程使用的时间片以及进程处于阻塞状态所花费的时间(例如等待I/O完成)。
  • User:是进程在用户模式代码(内核之外)中花费的CPU时间。这仅指执行进程时实际使用的CPU时间,其他进程和进程阻塞的时间不计入该数值。
  • Sys:是进程在内核中花费的CPU时间。这意味着在内核中执行系统调用所花费的CPU时间,与仍在用户空间运行的库代码不同。和user一样,这只是进程使用的CPU时间。

统计信息的来源

核心代码

sleep 系统调用示例

1
2
3
4
5
6
7
8
#define _XOPEN_SOURCE 700
#include <stdlib.h>
#include <unistd.h>

int main(void) {
sleep(1);
return EXIT_SUCCESS;
}

多线程示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

uint64_t niters;

void* my_thread(void *arg) {
uint64_t *argument, i, result;
argument = (uint64_t *)arg;
result = *argument;
for (i = 0; i < niters; ++i) {
result = (result * result) - (3 * result) + 1;
}
*argument = result;
return NULL;
}

int main(int argc, char **argv) {
size_t nthreads;
pthread_t *threads;
uint64_t rc, i, *thread_args;

/* CLI args. */
if (argc > 1) {
niters = strtoll(argv[1], NULL, 0);
} else {
niters = 1000000000;
}
if (argc > 2) {
nthreads = strtoll(argv[2], NULL, 0);
} else {
nthreads = 1;
}
threads = malloc(nthreads * sizeof(*threads));
thread_args = malloc(nthreads * sizeof(*thread_args));

/* Create all threads */
for (i = 0; i < nthreads; ++i) {
thread_args[i] = i;
rc = pthread_create(
&threads[i],
NULL,
my_thread,
(void*)&thread_args[i]
);
assert(rc == 0);
}

/* Wait for all threads to complete */
for (i = 0; i < nthreads; ++i) {
rc = pthread_join(threads[i], NULL);
assert(rc == 0);
printf("%" PRIu64 " %" PRIu64 "\n", i, thread_args[i]);
}

free(threads);
free(thread_args);
return EXIT_SUCCESS;
}

sendfile 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv) {
char *source_path, *dest_path;
int source, dest;
struct stat stat_source;
if (argc > 1) {
source_path = argv[1];
} else {
source_path = "sendfile.in.tmp";
}
if (argc > 2) {
dest_path = argv[2];
} else {
dest_path = "sendfile.out.tmp";
}
source = open(source_path, O_RDONLY);
assert(source != -1);
dest = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
assert(dest != -1);
assert(fstat(source, &stat_source) != -1);
assert(sendfile(dest, source, 0, stat_source.st_size) != -1);
assert(close(source) != -1);
assert(close(dest) != -1);
return EXIT_SUCCESS;
}

最佳实践

  • 判断程序类型:通过比较realusersys的时间,可以判断程序是CPU密集型还是I/O密集型。如果user + sys接近real,则程序可能是CPU密集型;如果real远大于user + sys,则程序可能是I/O密集型。
  • 评估并行效率:对于多线程程序,检查user时间是否超过real时间,以及userreal的比例是否接近核心数,可评估并行化的有效性。

常见问题

realuser + sys 不相等的原因

  • 多核心并行:在多核系统中,usersys时间(以及它们的总和)可能会超过real时间,因为不同的线程或进程可以并行运行。
  • 阻塞等待:进程在等待I/O完成或其他资源时,real时间会增加,但usersys时间不会计算等待时间,导致real > user + sys

不同实现的time命令

  • Bash关键字:在Ubuntu上直接使用time <cmd>,使用的是Bash关键字,其实现使用gettimeofday()getrusage()(如果可用),否则使用times()
  • GNU Coreutils:使用/usr/bin/time时,使用GNU Coreutils实现,会根据情况使用非POSIX的BSD wait3调用或timesgettimeofday

time(1)输出中'real'、'user'和'sys'的含义
https://119291.xyz/posts/2025-05-16.meanings-of-real-user-and-sys-in-time-output/
作者
ww
发布于
2025年5月16日
许可协议