为什么psutil CPU使用率太低?

2024-05-14 23:35:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我已经用Python编写了一个性能监视器函数,它使用psutil来测量CPU、GPU和磁盘I/O,以便对另一个应用程序进行基准测试。在我的初始测试中,它工作得非常好,但我发现当它接近100%时,它明显低于报告的CPU使用率。我正在寻求帮助,找出为什么会发生这种情况

这个函数可能看起来过于复杂,但我尝试做两件事:实时显示当前使用情况;并在末尾报告最高的5秒滚动平均值。这是因为我无法预测子流程需要多长时间,我想消除短期波动

下面是该函数的简化版本。在本例中,我删除了所有GPU和磁盘测量,并稍微更改了打印语句以进行调试。虽然我尝试每秒采样一次,但我看到elapsed时间有时在CPU使用率非常高的情况下波动高达1.8

这是在Windows 10上运行的。任务管理器显示恒定的100%CPU使用率,没有波动,如果我在另一个窗口中运行psutil.cpu_times_percent(),该窗口也显示100%,而我的函数在这里显示大约90%

import psutil
import time
import shlex
import subprocess
from concurrent.futures import ThreadPoolExecutor


def perf_monitor(proc):
    # measure CPU, GPU, and Disk R/W every 1 seconds, and calculate a rolling average every 5 seconds
    # return the maximum of each rolling average
    rolling_interval = 5
    sample_interval = 1
    rolling_samples = []
    stats = {
        'cpu': 0,
    }
    cpu_count = psutil.cpu_count()

    while True: # loop until subprocess is finished
        # reset starting values
        start = time.time()
        cpu1 = psutil.cpu_times()
        cpu1 = cpu1.user + cpu1.system

        # measure again after the interval
        time.sleep(sample_interval)
        cpu2 = psutil.cpu_times()
        cpu2 = cpu2.user + cpu2.system
        
        # list starts at zero and counts up, then remains at 5
        elapsed = time.time() - start # may be slightly longer than sample_interval
        rolling_samples.append({
            'cpu': (cpu2 - cpu1) / cpu_count / elapsed,
        })
        # skip reporting for the first 5 seconds warm up
        if len(rolling_samples) < rolling_interval:
            continue
        
        # get the rolling average over a defined interval (5 seconds)
        rolling_avg_cpu = sum([sample['cpu'] for sample in rolling_samples]) / rolling_interval

        print([sample['cpu'] for sample in rolling_samples], elapsed) # for debugging
        print(f"CPU: {rolling_avg_cpu:.1%}  ", end='\n',)

        # update each stat only if it has increased
        if rolling_avg_cpu > stats['cpu']:
            stats['cpu'] = rolling_avg_cpu
        
        # remove oldest sample so we always keep 5
        del rolling_samples[0]

        # return stats when render is finished
        if proc.poll() is not None:
            return stats

if __name__ == "__main__":
    command = '"MyApp.exe'
    with ThreadPoolExecutor() as executor:
        proc = subprocess.Popen(shlex.split(command))
        thread = executor.submit(perf_monitor, proc)
        stats = thread.result()
    
    print(stats)

Tags: sample函数importiftimestatscpupsutil

热门问题