如何为 Linux 程序计时

想分析一个 Linux 程序运行需要多少挂钟时间、内核时间等?无论是性能测试、代码优化,还是出于一般好奇心,本快速指南都将帮助您入门!
定时 Linux 程序
为 Linux 程序计时有助于了解花费了多少时间。通用的 Linux time
命令可用于此目的。 time
命令测量真实时间(即挂钟时间)、用户时间和系统时间。用户时间是程序在用户模式下运行的时间,或者换句话说,在内核之外。 sys时间是程序在内核内部运行的时间。
重要的是要注意,用户时间和系统时间分别是在用户模式和内核中花费的实际 CPU 时间。换句话说,当一个程序被阻塞了一段时间并且没有使用 CPU 时,这段时间将不计入 user 或 sys
时间。知道了这一点,我们就可以准确地测量使用了多少有效 CPU 时间(通过组合它们)。
Linux 时间工具
鉴于 user 和 sys
时间仅报告 CPU 时间,而 real 时间报告实际挂钟时间,(因此)很常见time
工具返回输出,其中 user + sys 的组合不等于 real 时间。计时 sleep
时可以看到一个例子:
time sleep 1

在这里,我们使用 time
工具为 sleep
命令计时。正如我们所见,我们的真实时间(1.001 秒)与我们的挂钟时间和请求时间(sleep 1
请求休眠一秒)非常匹配。我们还看到,整个命令只需要花费极少的 CPU 时间:结合 user + sys 时间,我们看到只花费了 0.001 秒。
我们还可以(可能是错误的)推断内核未参与该命令,因为 sys 时间实际上为 0。但是,正如 time
手册所述:“当命令的运行时间几乎为零,某些值(例如,CPU 使用百分比)可能报告为零(这是错误的)或问号。”
使用时间进行性能测量
我们可以使用 time
来评估给定的操作将花费多长时间(即挂钟时间)以及它们在此过程中消耗了多少 CPU 时间。作为一个简单的例子,我们可以评估是否有任何文件系统缓存在我们的系统上运行。为此,我们可以跳转到 /usr
目录,在普通的 Linux 安装中,该目录可以轻松容纳 200k 到 500k 的文件。
到达那里后,我们可以使用 find
工具,按 time
计时来评估扫描所有文件夹并列出 /usr 中的所有文件需要多长时间
目录:
cd /usr
time find . >/dev/null 2>&1

正如我们所见,列出 /usr
目录(及其下方)中的所有文件需要 12.484 秒。我们将命令的 stdout 输出(标准输出)重定向到 >/dev/null
,并将任何 stderr 错误(标准错误)重定向到 /dev/null
,方法是使用从 stderr 重定向到 stdout,即 2>&1
。
我们还看到我们的 CPU 时间是 1.043 秒(用户)+ 2.908 秒(系统),总共 3.951 秒的 CPU 时间。
让我们通过清除 inode(和其他)缓存来再次测试它:
sync; echo 3 | sudo tee /proc/sys/vm/drop_caches
cd /usr
time find . >/dev/null 2>&1

第一个命令将删除 inode 缓存、dentries(目录条目)和页面缓存。这次结果回来得更快了,命令节省了 1.255 秒。可能基于物理磁盘的缓存在这里有所帮助。
为了演示 Linux 缓存的一般工作情况,让我们重新运行命令,但这次不删除 Linux 缓存:

完全不同!我们看到所有三个计时区域所需的时间都大大减少,我们的命令执行时间不到半秒!
使用时间进行代码优化
一旦我们对在命令行中使用 time
命令感到满意,我们就可以扩展它的用途来优化我们的 Bash 脚本和代码。例如,一些专业人士常用的一种方法是多次运行给定命令,例如运行 1000 次,然后计算这些运行的总(或平均)时间。
然后,可以使用替代命令。然后可以再次对该替代命令(或解决方案/实施——即,将多个命令组合成一段代码进行计时)进行计时。在 Linux 中(或者更具体地说在 Bash 编码等中),通常有很多方法可以解决给定的问题;通常有多种工具可用于获得/实现相同的结果。
测试哪一个执行得最好可以优化程序运行时间和其他潜在因素,如磁盘 I/O(减少磁盘磨损)或内存利用率(允许更多程序在同一实例上执行)。为了优化挂钟时间,某个工具平均使用以及该工具消耗的 CPU 时间(另一个重要的优化因素/考虑因素)可以通过 time
工具测量。
让我们探索一个使用命令行执行我们想要在我们的脚本之一中使用的命令的实际示例。该命令将获取进程列表并显示第二列。我们同时使用 awk
和 sed
来执行此操作,并运行每个命令 1000 次以查看整体性能差异。
time for ((i=1;i<=1000;i++)); do ps -ef | awk '{print $2}' >/dev/null 2>&1; done
time for ((i=1;i<=1000;i++)); do ps -ef | sed 's|^[^ ]+[ t]+||;s|[ t].*||' >/dev/null 2>&1; done

虽然看起来更复杂(它使用双正则表达式来解析第二列),但就挂钟时间而言,我们的第二个命令比我们的第一个命令快一点。
使用非常相似的设置(即 time for ((i=1;i<=1000;i++)); do command_to_be_timed >/dev/null 2>&1; done
其中 command_to_be_timed 是要执行的命令测试挂钟或 CPU 时间),可以对任何命令或一组命令进行时间测试(就像这里的情况;我们同时使用了 ps
和 awk
/ sed
命令)。
对几个耗时的命令(在任何 Linux 脚本中)采取这些步骤将帮助我们减少整体运行时间和/或(如果您优化以减少 CPU 时间)我们脚本的系统负载。
如果您想了解有关正则表达式的更多信息,您可能会对如何使用正则表达式和 sed 流编辑器修改文本感兴趣。
包起来
在本文中,我们探讨了 Linux time
命令。我们阐明了真实、用户 和系统 时间的含义,以及后两者与 CPU 使用率的关系。我们还回顾了几个如何以实际方式使用时间的例子。
如果您喜欢阅读本文,请查看断言、错误和崩溃:有什么区别?什么是 Stack Smashing?可以修复吗?