Skip to content

[perf-scan] 2026-08-16 -- performance opportunities found #2585

Description

@github-actions

Performance Scan - 2026-08-16

Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
3 finding(s) identified.

Findings

[C] Unnecessary sort on every cache-miss pattern lookup -- compilation/context_optimizer.py:927

  • Current: O(D log D) sort of _directory_cache.items() on every cache-miss call to _find_matching_directories, where D = number of project directories. The sorted iterator is consumed into an unordered set[Path], making the sort outcome irrelevant to correctness.
  • Proposed: O(D) pass -- remove sorted() and iterate self._directory_cache.items() directly.
  • Fix: Replace for directory, analysis in sorted(self._directory_cache.items()): with for directory, analysis in self._directory_cache.items(): at line 927. The pattern cache (line 913) already ensures this path is taken at most once per unique pattern per analysis run, but removing the redundant sort eliminates a hidden O(D log D) cost on first-time pattern resolution for large projects.

[B] O(n^2) list membership test in dep-list union -- deps/plugin_parser.py:994-997

  • Current: if entry not in existing (line 996) performs a linear scan through the growing existing list for each entry in new_entries. For M new entries and N pre-existing entries this is O(M*N) comparisons. Called for every plugin manifest merge at pack / install time.
  • Proposed: O(M+N) with a parallel seen-set built once before the loop.
  • Fix: Before the loop, build seen = {json.dumps(e, sort_keys=True) if isinstance(e, dict) else e for e in existing} and replace the membership test with if (json.dumps(entry, sort_keys=True) if isinstance(entry, dict) else entry) not in seen:, appending to both existing and seen. This keeps the list for ordered output while giving O(1) duplicate checks.

[B] O(n*m) prefix scan in file filter -- bundle/lockfile_enrichment.py:150

  • Current: [f for f in deployed_files if any(f.startswith(p) for p in prefixes)] iterates every deployed file against every prefix. For a bundle with F deployed files and P prefixes, this is O(F*P) string prefix checks. Called at pack / export time.
  • Proposed: O(F log P) with sorted prefixes and bisect, or O(F*P) with early-termination which is already in place via any(). Low priority since P is typically < 10, but scales poorly as target count grows.
  • Fix: Sort prefixes once before the comprehension (they are already deduplicated at lines 134-140) and replace the any(...) with a bisect-based helper that finds the longest matching prefix in O(log P) per file.

Scan coverage

  • src/apm_cli/ (430 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 · 116.7 AIC · ⌖ 8.74 AIC · ⊞ 7.3K ·

  • expires on Aug 23, 2026, 1:41 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/automationAutomation script, workflow, gh-aw, dependabot config.type/performanceLatency, throughput, memory, install time.

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions