[转]CPU的等待有多久?

本文通过分析现代个人电脑中CPU及各类子系统的运行速度,包括延迟和数据吞吐量,揭示了不同组件间的性能差异。文中详细介绍了CPU、内存、缓存、硬盘等部件的工作原理,并通过生动的比喻帮助理解。

原文标题:What Your Computer Does While You Wait

原文地址:http://duartes.org/gustavo/blog/

[注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。]

本文以一个现代的、实际的个人电脑为对象,分析其中CPU(Intel Core 2 Duo 3.0GHz)以及各类子系统的运行速度——延迟和数据吞吐量。通过粗略的估算PC各个组件的相对运行速度,希望能给大家留下一个比较直观的印象。本文中的数据来自实际应用,而非理论最大值。时间的单位是纳秒(ns,十亿分之一秒),毫秒(ms,千分之一秒),和秒(s)。吞吐量的单位是兆字节(MB)和千兆字节(GB)。让我们先从CPU和内存开始,下图是北桥部分:   

第一个令人惊叹的事实是:CPU快得离谱。在Core 2 3.0GHz上,大部分简单指令的执行只需要一个时钟周期,也就是1/3纳秒。即使是真空中传播的光,在这段时间内也只能走10厘米(约4英寸)。把上述事实记在心中是有好处的。当你要对程序做优化的时候就会想到,执行指令的开销对于当今的CPU而言是多么的微不足道。   

当CPU运转起来以后,它便会通过L1 cache和L2 cache对系统中的主存进行读写访问。cache使用的是静态存储器(SRAM)。相对于系统主存中使用的动态存储器(DRAM),cache读写速度快得多、造价也高昂得多。cache一般被放置在CPU芯片的内部,加之使用昂贵高速的存储器,使其给CPU带来的延迟非常低。在指令层次上的优化(instruction-level optimization),其效果是与优化后代码的大小息息相关。由于使用了高速缓存技术(caching),那些能够整体放入L1/L2 cache中的代码,和那些在运行时需要不断调入/调出(marshall into/out of)cache的代码,在性能上会产生非常明显的差异。

正常情况下,当CPU操作一块内存区域时,其中的信息要么已经保存在L1/L2 cache,要么就需要将之从系统主存中调入cache,然后再处理。如果是后一种情况,我们就碰到了第一个瓶颈,一个大约250个时钟周期的延迟。在此期间如果CPU没有其他事情要做,则往往是处在停机状态的(stall)。为了给大家一个直观的印象,我们把CPU的一个时钟周期看作一秒。那么,从L1 cache读取信息就好像是拿起桌上的一张草稿纸(3秒);从L2 cache读取信息则是从身边的书架上取出一本书(14秒);而从主存中读取信息则相当于走到办公楼下去买个零食(4分钟)。

主存操作的准确延迟是不固定的,与具体的应用以及其他许多因素有关。比如,它依赖于列选通延迟(CAS)以及内存条的型号,它还依赖于CPU指令预取的成功率。指令预取可以根据当前执行的代码来猜测主存中哪些部分即将被使用,从而提前将这些信息载入cache。

看看L1/L2 cache的性能,再对比主存,就会发现:配置更大的cache或者编写能更好的利用cache的应用程序,会使系统的性能得到多么显著的提高。如果想进一步了解有关内存的诸多信息,读者可以参阅Ulrich Drepper所写的一篇经典文章《What Every Programmer Should Know About Memory》。

人们通常把CPU与内存之间的瓶颈叫做冯·诺依曼瓶颈(von Neumann bottleneck)。当今系统的前端总线带宽约为10GB/s,看起来很令人满意。在这个速度下,你可以在1秒内从内存中读取8GB的信息,或者10纳秒内读取100字 节。遗憾的是,这个吞吐量只是理论最大值(图中其他数据为实际值),而且是根本不可能达到的,因为主存控制电路会引入延迟。在做内存访问时,会遇到很多零 散的等待周期。比如电平协议要求,在选通一行、选通一列、取到可靠的数据之前,需要有一定的信号稳定时间。由于主存中使用电容来存储信息,为了防止因自然 放电而导致的信息丢失,就需要周期性的刷新它所存储的内容,这也带来额外的等待时间。某些连续的内存访问方式可能会比较高效,但仍然具有延时。而那些随机 的内存访问则消耗更多时间。所以延迟是不可避免的。

图中下方的南桥连接了很多其他总线(如:PCI-E, USB)和外围设备:

  令人沮丧的是,南桥管理了一些反应相当迟钝的设备,比如硬盘。就算是缓慢的系统主存,和硬盘相比也可谓速度如飞了。继续拿办公室做比喻,等待硬盘寻道的时间相当于离开办公大楼并开始长达一年零三个月的环球旅行。这就解释了为何电脑的大部分工作都受制于磁盘I/O,以及为何数据库的性能在内存缓冲区被耗尽后会陡然下降。同时也解释了为何充足的RAM(用于缓冲)和高速的磁盘驱动器对系统的整体性能如此重要。

虽然磁盘的"连续"存取速度确实可以在实际使用中达到,但这并非故事的全部。真正令人头疼的瓶颈在于寻道操作,也就是在磁盘表面移动读写磁头到正确的磁道上,然后再等待磁盘旋转到正确的位置上,以便读取指定扇区内的信息。RPM(每分钟绕转次数)用来指示磁盘的旋转速度:RPM越大,耽误在寻道上的时间就越少,所以越高的RPM意味着越快的磁盘。这里有一篇由两个Stanford的研究生写的很酷的文章,其中讲述了寻道时间对系统性能的影响:《Anatomy of a Large-Scale Hypertextual Web Search Engine》

当磁盘驱动器读取一个大的、连续存储的文件时会达到更高的持续读取速度,因为省去了寻道的时间。文件系统的碎片整理器就是用来把文件信息重组在连续的数据块 中,通过尽可能减少寻道来提高数据吞吐量。然而,说到计算机实际使用时的感受,磁盘的连续存取速度就不那么重要了,反而应该关注驱动器在单位时间内可以完 成的寻道和随机I/O操作的次数。对此,固态硬盘可以成为一个很棒的选择。

硬盘的cache也有助于改进性能。虽然16MB的cache只能覆盖整个磁盘容量的0.002%,可别看cache只有这么一点大,其效果十分明显。它可以把一组零散的写入操作合成一个,也就是使磁盘能够控制写入操作的顺序,从而减少寻道的次数。同样的,为了提高效率,一系列读取操作也可以被重组,而且操作系统和驱动器固件(firmware)都会参与到这类优化中来。

最后,图中还列出了网络和其他总线的实际数据吞吐量。火线(fireware)仅供参考,Intel X48芯片组并不直接支持火线。我们可以把Internet看作是计算机之间的总线。去访问那些速度很快的网站(比如google.com),延迟大约45毫秒,与硬盘驱动器带来的延迟相当。事实上,尽管硬盘比内存慢了5个数量级,它的速度与Internet是在同一数量级上的。目前,一般家用网络的带宽还是要落后于硬盘连续读取速度的,但"网络就是计算机"这句话可谓名符其实。如果将来Internet比硬盘还快了,那会是个什么景象呢?

我希望这些图片能对您有所帮助。当这些数字一起呈现在我面前时,真的很迷人,也让我看到了计算机技术发展到了哪一步。前文分开的两个图片只是为了叙述方便,我把包含南北桥的整张图片也贴出来,供您参考。

<think>好的,用户现在想了解如何在Linux中计算进程的CPU使用率百分比,需要公式和方法。之前我已经给出了一些方法,比如使用top、ps、/proc文件系统等,但用户现在可能希望更详细的解释,特别是基于/proc的数据进行计算,并且要结合提供的引用内容。 首先,我需要回顾用户提供的引用内容。引用[4]提到进程的CPU使用率等于进程CPU时间除以进程运行时间的百分比,而具体的计算步骤涉及到从/proc/pid/stat获取utime和stime,以及从/proc/uptime获取系统运行时间,再减去进程的启动时间。引用[3]中有一个例子,用1减去空闲时间的比例来计算CPU使用率,但这里可能更关注进程级别的计算。引用[5]解释了CPU百分比是基于时间片的繁忙程度,这和进程的CPU时间累积有关。 用户的问题明确要求公式和方法,所以需要详细列出步骤,可能还要包括代码示例。根据之前的知识,计算进程CPU使用率通常需要两个时间点的数据,计算差值然后除以总时间差,再乘以100得到百分比。需要确认公式是否正确,并确保引用的来源正确标注。 需要注意的是,用户可能希望看到两种方法:一种是使用现有的工具如ps或top,另一种是手动计算,可能用于脚本或编程。在之前的回答中已经提到了ps和/proc的方法,但现在需要更详细地展开手动计算的步骤,尤其是如何从/proc文件系统中提取必要的数据。 另外,用户提供的引用中提到了pidstat工具,这可能在方法中作为一个选项提及。同时,用户可能对公式中的各个变量有疑问,比如utime、stime、starttime等,需要解释清楚这些参数的含义和获取方式。 还需要注意单位换的问题,因为/proc/uptime和/proc/pid/stat中的时间单位可能不同(秒 vs jiffies),需要统一单位。比如,系统运行时间通常以秒为单位,而进程的CPU时间和启动时间可能以jiffies(时钟滴答数)为单位,因此需要换。 总结步骤可能如下: 1. 获取进程的utime和stime(用户态和内核态CPU时间)。 2. 获取系统的总运行时间。 3. 获取进程的启动时间。 4. 计算进程的总CPU时间。 5. 计算进程的运行时间(系统运行时间 - 进程启动时间)。 6. 使用公式:CPU使用率百分比 = (进程总CPU时间 / 进程运行时间) * 100。 在实现时,需要注意两次采样,间隔一段时间,然后计算差值,以避免累计值的问题。这样可以得到实时的CPU使用率。 可能还需要解释jiffies到秒的换,通常每秒的jiffies数可以通过sysconf(_SC_CLK_TCK)获取,一般为100。 最后,要确保所有引用都正确对应,比如引用[4]对应计算过程,引用[5]对应时间片的概念,引用[2]提到pidstat工具的使用。这样用户在参考时能明确每个步骤的来源。</think>在Linux中计算进程的CPU使用率百分比需要结合进程CPU时间和系统时间统计,以下是详细的公式和实现方法: --- ### 一、核心计算公式 进程CPU使用率百分比计算公式为: $$ \text{CPU\%} = \frac{\Delta (\text{进程总CPU时间})}{\Delta (\text{系统总运行时间})} \times 100 $$ 其中: - $\Delta (\text{进程总CPU时间})$ = 两次采样的进程CPU时间差值(单位:jiffies) - $\Delta (\text{系统总运行时间})$ = 两次采样的系统总运行时间差值(单位:jiffies)[^4] --- ### 二、数据来源与关键参数 #### 1. **进程CPU时间**(来自`/proc/[PID]/stat`) - **第14列 `utime`**:用户态CPU时间(jiffies) - **第15列 `stime`**:内核态CPU时间(jiffies) - **总CPU时间**:$\text{pct} = \text{utime} + \text{stime}$[^4] #### 2. **系统总运行时间**(来自`/proc/uptime`) - 第一列 `uptime`:系统总运行时间(秒) #### 3. **进程启动时间**(来自`/proc/[PID]/stat`) - **第22列 `starttime`**:进程启动时刻的系统时钟计数(jiffies) --- ### 三、手动计算步骤(编程实现) #### 步骤1:获取初始数据 ```bash # 读取系统时钟频率(每秒的jiffies数) CLK_TCK=$(getconf CLK_TCK) # 通常为100 # 第一次采样 prev_pct=$(awk '{print $14+$15}' /proc/[PID]/stat) prev_total=$(awk '{print $1}' /proc/uptime | tr '.' ' ') ``` #### 步骤2:等待采样间隔 ```bash sleep 1 # 采样间隔1秒(可调整) ``` #### 步骤3:获取第二次数据 ```bash current_pct=$(awk '{print $14+$15}' /proc/[PID]/stat) current_total=$(awk '{print $1}' /proc/uptime | tr '.' ' ') ``` #### 步骤4:计算CPU使用率 ```bash delta_pct=$((current_pct - prev_pct)) delta_total=$((current_total - prev_total)) cpu_usage=$(echo "scale=2; ($delta_pct / $CLK_TCK) / $delta_total * 100" | bc) echo "CPU Usage: ${cpu_usage}%" ``` --- ### 四、快速工具方法 #### 1. 使用`ps`命令 ```bash ps -o %cpu -p [PID] # 直接显示百分比[^4] ``` #### 2. 使用`pidstat`(推荐) ```bash pidstat -p [PID] 1 5 # 每1秒采样1次,共5次[^2] ``` 输出示例: ``` %usr %system %guest %wait %CPU 12.50 3.20 0.00 1.80 17.50 ``` #### 3. 使用`top`动态监控 ```bash top -p [PID] # 实时显示进程的%CPU列[^1] ``` --- ### 五、关键概念说明 | 参数 | 含义 | 来源文件 | |-------------|-------------------------------------|-------------------| | `utime` | 进程在用户态消耗的CPU时间(jiffies) | `/proc/[PID]/stat`| | `stime` | 进程在内核态消耗的CPU时间(jiffies) | `/proc/[PID]/stat`| | `starttime` | 进程启动时的系统时钟计数(jiffies) | `/proc/[PID]/stat`| | `uptime` | 系统总运行时间(秒) | `/proc/uptime` | --- ### 六、计算示例 假设: - 第一次采样:`pct1=1000` jiffies,`total1=500`秒 - 第二次采样:`pct2=1100` jiffies,`total2=501`秒 - `CLK_TCK=100` 计算: $$ \text{CPU\%} = \frac{(1100-1000)/100}{501-500} \times 100 = 10\% $$ --- --相关问题--: 1. 如何通过`/proc`文件系统实时监控进程的CPU时间变化? 2. 为什么`ps`和`top`显示的CPU使用率有时会有差异? 3. 在多核CPU环境下,如何计算进程在单个核心上的CPU使用率?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值