From c0d149497fc88c53d200418c3198c22dc0d84eb3 Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Wed, 6 Jan 2021 09:52:25 +0100 Subject: [PATCH] Speed up cpu_frequncy() on Linux systems (#1851) The change is about using /proc/cpuinfo when available. It provides cached values for frequencies and one can fill up minimum and maximum frequency from /sys/devices/system/cpu/cpufreq/policy/* sub-system (which is fast). Fixes #1851. --- psutil/_pslinux.py | 37 +++++++++++++++++++++---------------- psutil/tests/test_linux.py | 4 ---- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index ff8d924767..de12990394 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -719,6 +719,17 @@ def cpu_stats(): ctx_switches, interrupts, soft_interrupts, syscalls) +def _cpu_get_cpuinfo_freq(): + """Return current CPU frequency from cpuinfo if available. + """ + ret = [] + with open_binary('%s/cpuinfo' % get_procfs_path()) as f: + for line in f: + if line.lower().startswith(b'cpu mhz'): + ret.append(float(line.split(b':', 1)[1])) + return ret + + if os.path.exists("/sys/devices/system/cpu/cpufreq/policy0") or \ os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq"): def cpu_freq(): @@ -726,13 +737,19 @@ def cpu_freq(): Contrarily to other OSes, Linux updates these values in real-time. """ + cpuinfo_freqs = _cpu_get_cpuinfo_freq() paths = sorted( glob.glob("/sys/devices/system/cpu/cpufreq/policy[0-9]*") or glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq")) ret = [] pjoin = os.path.join - for path in paths: - curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None) + for i, path in enumerate(paths): + if len(paths) == len(cpuinfo_freqs): + # take cached value from cpuinfo if available, see: + # https://github.com/giampaolo/psutil/issues/1851 + curr = cpuinfo_freqs[i] + else: + curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None) if curr is None: # Likely an old RedHat, see: # https://github.com/giampaolo/psutil/issues/1071 @@ -746,24 +763,12 @@ def cpu_freq(): ret.append(_common.scpufreq(curr, min_, max_)) return ret -elif os.path.exists("/proc/cpuinfo"): +else: def cpu_freq(): """Alternate implementation using /proc/cpuinfo. min and max frequencies are not available and are set to None. """ - ret = [] - with open_binary('%s/cpuinfo' % get_procfs_path()) as f: - for line in f: - if line.lower().startswith(b'cpu mhz'): - key, value = line.split(b':', 1) - ret.append(_common.scpufreq(float(value), 0., 0.)) - return ret - -else: - def cpu_freq(): - """Dummy implementation when none of the above files are present. - """ - return [] + return [_common.scpufreq(value, 0., 0.) for value in _cpu_get_cpuinfo_freq()] # ===================================================================== diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 2f6065f596..0c6d498c81 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -768,18 +768,14 @@ def path_exists_mock(path): if path.startswith('/sys/devices/system/cpu/'): return False else: - if path == "/proc/cpuinfo": - flags.append(None) return os_path_exists(path) - flags = [] os_path_exists = os.path.exists try: with mock.patch("os.path.exists", side_effect=path_exists_mock): reload_module(psutil._pslinux) ret = psutil.cpu_freq() assert ret - assert flags self.assertEqual(ret.max, 0.0) self.assertEqual(ret.min, 0.0) for freq in psutil.cpu_freq(percpu=True):