diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py index ddef7bb0a1..a6e63c058c 100644 --- a/lib/_emerge/depgraph.py +++ b/lib/_emerge/depgraph.py @@ -62,6 +62,7 @@ from portage.util.digraph import digraph from portage.util.futures import asyncio from portage.util._async.TaskScheduler import TaskScheduler +from portage.util.portage_lru_cache import show_lru_cache_info from portage.versions import _pkg_str, catpkgsplit from portage.binpkg import get_binpkg_format @@ -11800,6 +11801,7 @@ def _spinner_stop(spinner, backtracked: int = -1, max_retries: int = -1): portage.writemsg_stdout( f"Dependency resolution took {darkgreen(time_fmt)} s{backtrack_info}.\n\n" ) + show_lru_cache_info() def backtrack_depgraph( diff --git a/lib/_emerge/post_emerge.py b/lib/_emerge/post_emerge.py index 6f1f1c243d..06fb8d1784 100644 --- a/lib/_emerge/post_emerge.py +++ b/lib/_emerge/post_emerge.py @@ -11,6 +11,7 @@ from portage.output import colorize from portage.util._dyn_libs.display_preserved_libs import display_preserved_libs from portage.util._info_files import chk_updated_info_files +from portage.util.portage_lru_cache import show_lru_cache_info from .chk_updated_cfg_files import chk_updated_cfg_files from .emergelog import emergelog @@ -169,3 +170,5 @@ def post_emerge(myaction, myopts, myfiles, target_root, trees, mtimedb, retval): if "--quiet" not in myopts and myaction is None and "@world" in myfiles: show_depclean_suggestion() + + show_lru_cache_info() diff --git a/lib/portage/process.py b/lib/portage/process.py index 3f2c48e927..d252cc7c4a 100644 --- a/lib/portage/process.py +++ b/lib/portage/process.py @@ -323,17 +323,19 @@ class EnvStats: env_largest_size: int +@lru_cache(1024) +def _encoded_length(s): + return len(os.fsencode(s)) + + def calc_env_stats(env) -> EnvStats: - @lru_cache(1024) - def encoded_length(s): - return len(os.fsencode(s)) env_size = 0 env_largest_name = None env_largest_size = 0 for env_name, env_value in env.items(): - env_name_size = encoded_length(env_name) - env_value_size = encoded_length(env_value) + env_name_size = _encoded_length(env_name) + env_value_size = _encoded_length(env_value) # Add two for '=' and the terminating null byte. total_size = env_name_size + env_value_size + 2 if total_size > env_largest_size: diff --git a/lib/portage/util/meson.build b/lib/portage/util/meson.build index 8a60617d6f..fed09e20df 100644 --- a/lib/portage/util/meson.build +++ b/lib/portage/util/meson.build @@ -21,6 +21,7 @@ py.install_sources( 'mtimedb.py', 'netlink.py', 'path.py', + 'portage_lru_cache.py', 'shelve.py', 'socks5.py', 'whirlpool.py', diff --git a/lib/portage/util/portage_lru_cache.py b/lib/portage/util/portage_lru_cache.py new file mode 100644 index 0000000000..4b02cbee04 --- /dev/null +++ b/lib/portage/util/portage_lru_cache.py @@ -0,0 +1,42 @@ +# Copyright 2025 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +import os +import portage + + +def show_lru_cache_info(): + if not os.environ.get("PORTAGE_SHOW_LRU_CACHE_INFO"): + return + + portage_lru_caches = { + portage.dep._use_reduce_cached: "use_reduce_cached", + portage.process._encoded_length: "encoded_length", + portage.versions.vercmp: "vercmp", + portage.versions.catpkgsplit: "catpkgsplit", + portage.eapi._get_eapi_attrs: "get_eapi_attrs", + } + + print("Portage @lru_cache information") + for method, name in portage_lru_caches.items(): + cache_info = method.cache_info() + + hits = cache_info.hits + misses = cache_info.misses + maxsize = cache_info.maxsize + currsize = cache_info.currsize + + total = hits + misses + if total: + hitratio = hits / total + else: + hitratio = 0 + + if maxsize: + utilization = currsize / maxsize + else: + utilization = 0 + + pretty_cache_info = f"hit ratio: {hitratio:.2%} (total: {total}, hits: {hits}, misses: {misses}) util: {utilization:.2%} ({maxsize} / {currsize})" + + print(f"{name}: {pretty_cache_info}")