Performance Scan - 2026-09-14
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
3 finding(s) identified.
Findings
[B] Linear scan inside a loop -- src/apm_cli/commands/uninstall/engine.py:189-260
- Current: O(n*m) --
_stage_shared_local_survivors loops over
packages_to_remove (n) and calls _surviving_local_refs_at_install_path
(line 196), which itself linearly scans all of surviving_dependencies (m)
on every call. The same helper is also called again per-package inside
_dry_run_uninstall (line 756), doubling the cost on --dry-run.
- Proposed: O(n+m) -- pre-index
surviving_dependencies once into a
dict[install_path, list[DependencyReference]] before either loop, then
do an O(1) dict lookup per package instead of an O(m) scan.
- Fix: Build a
install_path -> [local survivors] dict once outside both
loops in engine.py and replace the repeated calls to
_surviving_local_refs_at_install_path with a lookup against that dict.
[E] Heavy top-level imports on CLI command modules -- src/apm_cli/commands/install.py:1-53
- Current:
apm.commands.install is imported eagerly by
src/apm_cli/cli.py:38 for every CLI invocation, and it in turn performs
53 top-level imports (agent_plugins, copilot_plugins, install.argv,
install.artifactory_resolver, install.dry_run_plan, install.errors,
install.gitlab_resolver, install.helpers.ref_reuse,
install.manifest_helpers, install.finalization, install.helpers.security_scan,
etc.) that are only exercised when the user actually runs apm install.
Every other subcommand (e.g. apm view, apm config) pays this import
cost at process startup.
- Proposed: defer the install-only heavy imports (artifactory_resolver,
gitlab_resolver, dry_run_plan, manifest_helpers, security_scan) to inside
the install() command function body, keeping only what's needed for
Click registration (the install callable itself) at module import time.
- Fix: Move the imports in
src/apm_cli/commands/install.py lines 14-51
that are only referenced inside the install/helper function bodies to
local imports inside those functions, following the existing pattern
already used for close_install_contexts deferral elsewhere in the file.
[C] Unconditional directory walk fallback -- src/apm_cli/install/services.py:863-880
- Current: O(n) unconditional -- when
pack_files is empty (older bundle
without bundle_files metadata), the code falls back to
bundle_dir.rglob("*") and computes a full sha256 hash of every file in
the bundle on every deploy for that bundle, with no caching between
target loops in the same install run.
- Proposed: this fallback is correctly gated behind
if not pack_files:
already (fast-path skip when bundle metadata is present), so this is a
low-priority, borderline finding -- it only fires for legacy bundles
lacking bundle_files, not on every install. Noting for awareness only;
no action required unless legacy-bundle installs become a hot path.
Scan coverage
- src/apm_cli/ (484 files scanned)
- Patterns checked: A (quadratic loops), B (linear scan in loop),
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)
Generated by Daily Performance Scanner · copilot · auto · 105.2 AIC · ⌖ 6.75 AIC · ⊞ 10.1K · ◷
Performance Scan - 2026-09-14
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
3 finding(s) identified.
Findings
[B] Linear scan inside a loop -- src/apm_cli/commands/uninstall/engine.py:189-260
_stage_shared_local_survivorsloops overpackages_to_remove(n) and calls_surviving_local_refs_at_install_path(line 196), which itself linearly scans all of
surviving_dependencies(m)on every call. The same helper is also called again per-package inside
_dry_run_uninstall(line 756), doubling the cost on--dry-run.surviving_dependenciesonce into adict[install_path, list[DependencyReference]]before either loop, thendo an O(1) dict lookup per package instead of an O(m) scan.
install_path -> [local survivors]dict once outside bothloops in
engine.pyand replace the repeated calls to_surviving_local_refs_at_install_pathwith a lookup against that dict.[E] Heavy top-level imports on CLI command modules -- src/apm_cli/commands/install.py:1-53
apm.commands.installis imported eagerly bysrc/apm_cli/cli.py:38for every CLI invocation, and it in turn performs53 top-level imports (agent_plugins, copilot_plugins, install.argv,
install.artifactory_resolver, install.dry_run_plan, install.errors,
install.gitlab_resolver, install.helpers.ref_reuse,
install.manifest_helpers, install.finalization, install.helpers.security_scan,
etc.) that are only exercised when the user actually runs
apm install.Every other subcommand (e.g.
apm view,apm config) pays this importcost at process startup.
gitlab_resolver, dry_run_plan, manifest_helpers, security_scan) to inside
the
install()command function body, keeping only what's needed forClick registration (the
installcallable itself) at module import time.src/apm_cli/commands/install.pylines 14-51that are only referenced inside the
install/helper function bodies tolocal imports inside those functions, following the existing pattern
already used for
close_install_contextsdeferral elsewhere in the file.[C] Unconditional directory walk fallback -- src/apm_cli/install/services.py:863-880
pack_filesis empty (older bundlewithout
bundle_filesmetadata), the code falls back tobundle_dir.rglob("*")and computes a fullsha256hash of every file inthe bundle on every deploy for that bundle, with no caching between
target loops in the same install run.
if not pack_files:already (fast-path skip when bundle metadata is present), so this is a
low-priority, borderline finding -- it only fires for legacy bundles
lacking
bundle_files, not on every install. Noting for awareness only;no action required unless legacy-bundle installs become a hot path.
Scan coverage
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)