Skip to content

Commit

Permalink
Speed up cpu_frequncy() on Linux systems (#1851)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
marxin committed Jan 6, 2021
1 parent 51eb1da commit c0d1494
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 20 deletions.
37 changes: 21 additions & 16 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,20 +719,37 @@ 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():
"""Return frequency metrics for all CPUs.
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
Expand All @@ -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()]


# =====================================================================
Expand Down
4 changes: 0 additions & 4 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit c0d1494

Please sign in to comment.