Performance Scan - 2026-08-14
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
2 finding(s) identified.
Findings
[A] Cumulative Re-Sort at Every BFS Level -- src/apm_cli/deps/apm_resolver.py:728-734
- Current: O(D x N log N) --
_select_dependency_winners(winner_candidates) is called at every BFS depth level with the full accumulated list of ALL previously seen nodes. winner_candidates grows by one level per iteration, but the sort always starts from scratch, touching every node seen so far.
- Proposed: O(N log N) total -- maintain
winner_ids as an incrementally-updated dict; on each new node, apply the same depth-first, get_id() tie-break rule without re-sorting the entire history.
- Fix: Replace the per-level
_select_dependency_winners(winner_candidates) call with an incremental update: winner_ids.setdefault(node.dependency_ref.get_unique_key(), node.get_id()) once per new node (depth ordering is already guaranteed by BFS level batching), and remove the winner_candidates accumulator entirely.
[A] O(n) Linear Child Membership Check in BFS Resolution Loop -- src/apm_cli/deps/apm_resolver.py:698-700,819-820
- Current: O(k) per call --
all(child is not X for child in parent_node.children) scans the entire children list to verify a node is not already present before appending. Called at lines 698-700 and 819-820, once per dependency edge resolved in the BFS loop.
- Proposed: O(1) per call -- use a parallel set of child node IDs on DependencyNode.
- Fix: Add a
children_set: set[str] field to DependencyNode (populated alongside children.append); replace both all(child is not X for child in node.children) guards with X.get_id() not in parent_node.children_set.
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 · 110.6 AIC · ⌖ 6.18 AIC · ⊞ 7.3K · ◷
Performance Scan - 2026-08-14
Automated scan of src/apm_cli/ for algorithmic performance anti-patterns.
2 finding(s) identified.
Findings
[A] Cumulative Re-Sort at Every BFS Level -- src/apm_cli/deps/apm_resolver.py:728-734
_select_dependency_winners(winner_candidates)is called at every BFS depth level with the full accumulated list of ALL previously seen nodes.winner_candidatesgrows by one level per iteration, but the sort always starts from scratch, touching every node seen so far.winner_idsas an incrementally-updated dict; on each new node, apply the same depth-first, get_id() tie-break rule without re-sorting the entire history._select_dependency_winners(winner_candidates)call with an incremental update:winner_ids.setdefault(node.dependency_ref.get_unique_key(), node.get_id())once per new node (depth ordering is already guaranteed by BFS level batching), and remove thewinner_candidatesaccumulator entirely.[A] O(n) Linear Child Membership Check in BFS Resolution Loop -- src/apm_cli/deps/apm_resolver.py:698-700,819-820
all(child is not X for child in parent_node.children)scans the entire children list to verify a node is not already present before appending. Called at lines 698-700 and 819-820, once per dependency edge resolved in the BFS loop.children_set: set[str]field toDependencyNode(populated alongsidechildren.append); replace bothall(child is not X for child in node.children)guards withX.get_id() not in parent_node.children_set.Scan coverage
C (unconditional expensive ops), D (redundant config parsing),
E (heavy top-level imports), F (sequential independent I/O)