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 · ◷
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
_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 unorderedset[Path], making the sort outcome irrelevant to correctness.sorted()and iterateself._directory_cache.items()directly.for directory, analysis in sorted(self._directory_cache.items()):withfor 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
if entry not in existing(line 996) performs a linear scan through the growingexistinglist for eachentryinnew_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.seen = {json.dumps(e, sort_keys=True) if isinstance(e, dict) else e for e in existing}and replace the membership test withif (json.dumps(entry, sort_keys=True) if isinstance(entry, dict) else entry) not in seen:, appending to bothexistingandseen. 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
[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.any(). Low priority since P is typically < 10, but scales poorly as target count grows.prefixesonce before the comprehension (they are already deduplicated at lines 134-140) and replace theany(...)with a bisect-based helper that finds the longest matching prefix in O(log P) per file.Scan coverage
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)