本文地址:https://my.oschina.net/yumm007/blog/920412
前言
time 命令是开发过程中常用到的一个系统命令,主要是查看某个程序运行的时间分布。 一般用法为time ls
, 以下输出结果
real 0m0.004s
user 0m0.000s
sys 0m0.000s
其中 real 是指墙上时间,即命令从开始执行到执行结束的总时间。 user和 sys 时间是 CPU 真正花费在此程序上的时间,user 是用户态的时间,sys 是程序调用的系统调用(运行在内核态)所运行的时间。
当 user + sys >= real 时,说明存在密集型计算任务; 当 user + sys 远小于 real 时,说明存在较多的 IO 等待。
你运行的可能是假 time
time 命令位于 /usr/bin/目录下,可以通过which time
得到验证
which time
/usr/bin/time
但在 shell 里敲入 time命令,实际上运行的可能是 shell 内置的 time,可以通过命令type time
进行确认,
type time
time is a shell keyword
也可以通过查找版本号进行确认
time --version
-bash: --version: command not found
/usr/bin/time --version
GNU time 1.7
如果碰到了这种情况,需要手动敲入time 命令的全路径来运行真正的 time 命令。
time 的强大用法
-p
运行/usr/bin/time ls
,
/usr/bin/time ls
0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 872maxresident)k
0inputs+0outputs (0major+272minor)pagefaults 0swaps
信息比刚才的 time 输出的多,但是结果没有换行。如果想得到和shell 内置 time 通用的输出,可以运行如下命令
/usr/bin/time -p ls
real 0m0.004s
user 0m0.000s
sys 0m0.000s
参数 -p 是指使用格式"real %e\nuser %U\nsys %S\n"输出结果。
-f
通过命令 /usr/bin/time -f "real %e\nuser %U\nsys %S\n" sleep 1
也可以得到相关格式的输出
/usr/bin/time -f "real %e\nuser %U\nsys %S\n" sleep 1
real 1.00
user 0.00
sys 0.00
-f 支持很多参数,分别如下:
类型 | 格式 | 含义 |
---|---|---|
cpu | %E | 程序运行的墙上时间,格式为[hours:]minutes:seconds |
cpu | %e | 程序运行的墙上时间,格式为秒. |
cpu | %S | 程序花费在内核态的的时间 |
cpu | %U | 程序花费在用户态的时间 |
cpu | %P | 程序占用CPU的使用率,计算方法为 (%U + %S) / %E. |
mem | %M | Maximum resident set size of the process during its lifetime, in Kbytes. |
mem | %t | (Not in tcsh.) Average resident set size of the process, in Kbytes. |
mem | %K | Average total (data+stack+text) memory use of the process, in Kbytes. |
mem | %D | Average size of the process’s unshared data area, in Kbytes. |
mem | %p | (Not in tcsh.) Average size of the process’s unshared stack space, in Kbytes. |
mem | %X | Average size of the process’s shared text space, in Kbytes. |
mem | %Z | (Not in tcsh.) System’s page size, in bytes. This is a per-system constant, but varies between systems. |
mem | %F | 主缺页异常次数,每次缺页都需要从磁盘唤入 |
mem | %R | 次缺页异常,每次缺页都可以从内存缓存唤入 |
mem | %c | 被动放弃 CPU(时间片到、被高优先级任务抢占) 导致的上下文切换次数 |
mem | %w | 主动放弃 CPU(阻塞调用) 导致的上下文切换次数。 |
IO | %I | 通过文件系统读入的字节数 |
IO | %O | 通过文件系统写出的字节数 |
IO | %r | 通过网络接收到的消息数 |
IO | %s | 通过网络发出的消息数 |
IO | %k | 收到的中断数 |
IO | %C | 执行程序时的命令行函数 |
IO | %x | 程序返回值 |
-v
-v 参数将使time 输出足够详细的信息,而不需要指定上文提到的格式串。
/usr/bin/time -v ls
Command being timed: "ls"
User time (seconds): 0.00
System time (seconds): 0.00
Percent of CPU this job got: 0%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 864
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 271
Voluntary context switches: 1
Involuntary context switches: 1
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0
time命令在性能分析中的作用
先阐释几个概念
- 缺页异常
- 上下文切换
- CPU 时间
缺页异常
操作系统为每个进程都虚拟了4GB(在32位系统中) 的内存空间,但进程在某个时间片段真正使用的物理内存只是很小一部分(因为要和其他进程共同使用物理内存)。操作系统会把当前不使用的内存存入到虚拟内存中,当下次访问这些内存数据时,再把它们唤入到进程的范围地址内,此为次缺页异常;
虚拟内存管理器会维护虚拟内存的使用情况,定时将某些长期不使用的内存挪到硬盘,以便缓存新的内存页。当程序要访问的内存被挪到了硬盘上,此时操作系统需要将内存也从硬盘复制到内存中,映射到进程的访问地址中,此为主缺页异常。
主缺页异常涉及到硬盘 IO,开销比次缺页异常高出好几个等级。
上下文切换
属于上下文是指进程执行时的运行环境,包括当时的寄存器值、内存堆栈等信息,操作系统可以根据上下文完全恢复一个被打断的进程任务。 在程序代码从用户态切换到内核态时,或者发生进程切换,都会产生上下文切换。切换上下文时,操作系统需要为进程保存上下文信息,并在下次调度进程时恢复这些信息。
上下文切换分为主动(Voluntary context switches)和被动(Involuntary context switches)两种。主动上下文切换较多,说明存在较多的阻塞调用;被动上下文切换较多说明CPU 使用率高。
CPU 时间
CPU 时间分为real 时间,sys 时间,user 时间(含义在本文开头已经描述过)。 当 user + sys >= real 时,说明存在密集型计算任务; 当 user + sys 远小于 real 时,说明存在较多的 IO 等待。
性能衡量
- 次缺页异常较多,说明程序的内存布局相对合理,命中率高;当主缺页异常较多时,说明程序对内存的访问跳跃性大,命中率低。
- 上下文切换较多,将导致较多的时间用在上下文的保存和加载中。
- 处理缺页异常和切换上下文的时间既不算在 user 中,也不算在 sys 中。当发现 user + sys 远小于 real 时,则很可能大部分时间都耗费在这些地方,需要重点分析这两点。