Skip to content

Commit 5ba3d04

Browse files
author
Hudson Gerwing
committed
refactor: move arc stats call to a separate function to be less obtrusive
Signed-off-by: Hudson Gerwing <grownuphudson@gmail.com>
1 parent 24d06fa commit 5ba3d04

File tree

2 files changed

+41
-78
lines changed

2 files changed

+41
-78
lines changed

psutil/__init__.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1961,7 +1961,7 @@ def cpu_freq(percpu=False):
19611961
# =====================================================================
19621962

19631963

1964-
def virtual_memory(include_zfs_arc=False):
1964+
def virtual_memory():
19651965
"""Return statistics about system memory usage as a namedtuple
19661966
including the following fields, expressed in bytes:
19671967
@@ -2014,7 +2014,7 @@ def virtual_memory(include_zfs_arc=False):
20142014
On Windows 'available' and 'free' are the same.
20152015
"""
20162016
global _TOTAL_PHYMEM
2017-
ret = _psplatform.virtual_memory(include_zfs_arc)
2017+
ret = _psplatform.virtual_memory()
20182018
# cached for later use in Process.memory_percent()
20192019
_TOTAL_PHYMEM = ret.total
20202020
return ret
@@ -2036,6 +2036,14 @@ def swap_memory():
20362036
return _psplatform.swap_memory()
20372037

20382038

2039+
def apply_zfs_arcstats(vm_stats):
2040+
"""Apply ZFS ARC stats to virtual memory stats."""
2041+
# Only applicable to linux distros
2042+
if LINUX:
2043+
return _psplatform.apply_zfs_arcstats(vm_stats)
2044+
raise NotImplementedError("ZFS ARC stats are only available on Linux")
2045+
2046+
20392047
# =====================================================================
20402048
# --- disks/paritions related functions
20412049
# =====================================================================

psutil/_pslinux.py

+31-76
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,6 @@ class IOPriority(enum.IntEnum):
171171
svmem = namedtuple(
172172
'svmem', ['total', 'available', 'percent', 'used', 'free',
173173
'active', 'inactive', 'buffers', 'cached', 'shared', 'slab'])
174-
# psutil.zfs_arc_stats()
175-
szfsarc = namedtuple(
176-
'szfsarc', ['enabled', 'min', 'max', 'compressed', 'uncompressed',
177-
'size', 'header', 'anon', 'mfu', 'mru', 'other'])
178174
# psutil.disk_io_counters()
179175
sdiskio = namedtuple(
180176
'sdiskio', ['read_count', 'write_count',
@@ -419,15 +415,13 @@ def calculate_avail_vmem(mems):
419415
return int(avail)
420416

421417

422-
def virtual_memory(include_zfs_arc: bool = False):
418+
def virtual_memory():
423419
"""Report virtual memory stats.
424420
This implementation mimics procps-ng-3.3.12, aka "free" CLI tool:
425421
https://gitlab.com/procps-ng/procps/blob/
426422
24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791
427423
The returned values are supposed to match both "free" and "vmstat -s"
428424
CLI tools.
429-
If specifying the `include_zfs_arc` parameter, the implementation mimics
430-
"htop" CLI tool instead of "free" CLI tool.
431425
"""
432426
missing_fields = []
433427
mems = {}
@@ -529,29 +523,7 @@ def virtual_memory(include_zfs_arc: bool = False):
529523
# 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
530524
avail = free
531525

532-
# ZFS ARC memory consumption is not reported by /proc/meminfo.
533-
# Specifying `include_zfs_arc` will include reclaimable ZFS ARC
534-
# memory in the returned values.
535-
# N.B. this will make psutil match the output of "htop" instead
536-
# of "free" CLI tool.
537-
# See:
538-
# https://www.reddit.com/r/zfs/comments/ha0p7f/understanding_arcstat_and_free/
539-
# https://github.com/openzfs/zfs/issues/10255
540-
if include_zfs_arc:
541-
zfs = zfs_arc_stats()
542-
543-
# When accounting for zfs memory, we need to keep track of "shared"
544-
# So "used" memory is more relevant than "available"
545-
used += shared
546-
cached -= shared
547-
if zfs.enabled:
548-
shrinkable_size = max(zfs.size - zfs.min, 0)
549-
used -= shrinkable_size
550-
cached += shrinkable_size
551-
avail += shrinkable_size
552-
percent = usage_percent(used, total, round_=1)
553-
else:
554-
percent = usage_percent((total - avail), total, round_=1)
526+
percent = usage_percent((total - avail), total, round_=1)
555527

556528
# Warn about missing metrics which are set to 0.
557529
if missing_fields:
@@ -631,19 +603,11 @@ def swap_memory():
631603
return _common.sswap(total, used, free, percent, sin, sout)
632604

633605

634-
def zfs_arc_stats():
635-
"""Return ZFS ARC (Adaptive Replacement Cache) stats."""
636-
missing_fields = []
606+
def apply_zfs_arcstats(vm_stats: svmem):
607+
"""Apply ZFS ARC (Adaptive Replacement Cache) stats to
608+
input virtual memory call results"""
637609
mems = {}
638610

639-
def get_if_available(key):
640-
try:
641-
return mems[key]
642-
except KeyError:
643-
missing_fields.append(key)
644-
return 0
645-
646-
647611
with open_binary('%s/spl/kstat/zfs/arcstats' % get_procfs_path()) as f:
648612
for line in f:
649613
fields = line.split()
@@ -653,44 +617,35 @@ def get_if_available(key):
653617
# Not a key: value line
654618
continue
655619

656-
zfs_min = get_if_available(b'c_min')
657-
zfs_max = get_if_available(b'c_max')
658-
zfs_compressed = get_if_available(b'compressed_size')
659-
zfs_uncompressed = get_if_available(b'uncompressed_size')
660-
zfs_size = get_if_available(b'size')
661-
zfs_header = get_if_available(b'hdr_size')
662-
dbuf_size = get_if_available(b'dbuf_size')
663-
dnode_size = get_if_available(b'dnode_size')
664-
bonus_size = get_if_available(b'bonus_size')
665-
zfs_anon = get_if_available(b'anon_size')
666-
zfs_mfu = get_if_available(b'mfu_size')
667-
zfs_mru = get_if_available(b'mru_size')
668-
669-
zfs_enabled = zfs_size > 0
670-
zfs_other = dbuf_size + dnode_size + bonus_size if all([dbuf_size, dnode_size, bonus_size]) else 0
671-
672-
# Warn about missing metrics which are set to 0.
673-
if missing_fields:
674-
msg = "%s memory stats couldn't be determined and %s set to 0" % (
675-
", ".join(missing_fields),
676-
"was" if len(missing_fields) == 1 else "were",
677-
)
620+
try:
621+
zfs_min = mems[b'c_min']
622+
zfs_size = mems[b'size']
623+
except KeyError:
624+
msg = ("ZFS ARC memory stats couldn't be determined, "
625+
"no modification made to virtual memory stats")
678626
warnings.warn(msg, RuntimeWarning, stacklevel=2)
627+
zfs_min = zfs_size = 0
679628

680-
return szfsarc(
681-
zfs_enabled,
682-
zfs_min,
683-
zfs_max,
684-
zfs_compressed,
685-
zfs_uncompressed,
686-
zfs_size,
687-
zfs_header,
688-
zfs_anon,
689-
zfs_mfu,
690-
zfs_mru,
691-
zfs_other
692-
)
629+
# ZFS ARC memory consumption is not reported by /proc/meminfo.
630+
# Running this func will include reclaimable ZFS ARC
631+
# memory in the returned values.
632+
# N.B. this will make psutil match the output of "htop" instead
633+
# of "free" CLI tool.
634+
# See:
635+
# https://www.reddit.com/r/zfs/comments/ha0p7f/understanding_arcstat_and_free/
636+
# https://github.com/openzfs/zfs/issues/10255
693637

638+
# When accounting for zfs memory, we need to keep track of "shared"
639+
# So "used" memory is more relevant than "available"
640+
vm_stats.used += vm_stats.shared
641+
vm_stats.cached -= vm_stats.shared
642+
shrinkable_size = max(zfs_size - zfs_min, 0)
643+
vm_stats.used -= shrinkable_size
644+
vm_stats.cached += shrinkable_size
645+
vm_stats.available += shrinkable_size
646+
vm_stats.percent = usage_percent(vm_stats.used, vm_stats.total, round_=1)
647+
648+
return vm_stats
694649

695650

696651
# =====================================================================

0 commit comments

Comments
 (0)