Skip to content

perf(metrics): optimize LakosMetrics transitive dependency graph#1629

Merged
hankem merged 1 commit into
TNG:mainfrom
ThanosTsiamis:optimize-lakos-metrics
May 26, 2026
Merged

perf(metrics): optimize LakosMetrics transitive dependency graph#1629
hankem merged 1 commit into
TNG:mainfrom
ThanosTsiamis:optimize-lakos-metrics

Conversation

@ThanosTsiamis
Copy link
Copy Markdown
Contributor

Description

This Pull Request addresses the performance hotspot in LakosMetrics by overhauling how transitive reachability is computed within MetricsComponentDependencyGraph.

The Problem

Previously, getTransitiveDependenciesOf() executed a fresh Depth-First Search (DFS) from scratch for every single component. On complex architecture graphs with shared downstream subgraphs or deep/cyclic dependencies, this caused heavily redundant traversals, driving the runtime complexity up to O(V * (V + E)). It also introduced significant garbage collection pressure by instantiating new HashSet collections on every query.

The Solution

Instead of query-time DFS traversals, this PR introduces a robust, primitive-backed precomputation engine executed once during the graph's initialization:

  1. Cycle Detection (Kosaraju's Algorithm): determineStronglyConnectedComponents() runs a two-pass iterative DFS (forward to find finishing times, reverse on the transposed graph) to safely group cyclic dependencies into Strongly Connected Components (SCCs).
  2. DAG Condensation: The component graph is condensed into a Directed Acyclic Graph (DAG) where each node represents an isolated SCC.
  3. Topological BitSet Propagation: The DAG is sorted topologically, and downstream reachability is propagated using fast bitwise operations (BitSet.or()). This ensures that shared downstream paths are evaluated exactly once.
  4. O(1) Queries: The final reachability matrices are mapped back to their respective components and cached, changing getTransitiveDependenciesOf() from an expensive graph traversal into an O(1) map lookup.

Additional Robustness & Safeguards

  • Stack Safety: The algorithm completely avoids recursion in favor of an explicit iterative stack approach (ArrayDeque<TraversalFrame>), protecting the JVM against StackOverflowError on deep, linear dependency chains.
  • Cache Locality: Internal graph representations are flattened into primitive int[][], int[], and boolean[] arrays to optimize CPU cache locality and reduce memory footprint.
  • Duplicate Resilience: Rather than utilizing a strict ImmutableMap.Builder (which crashes with an IllegalArgumentException if duplicate elements are fed from upstream), the precomputation safely compiles results into an intermediate LinkedHashMap before generating the final immutable copy.
  • Semantic Parity: Existing cycle and self-reachability rules are perfectly preserved. A component only transitively reaches itself if it belongs to an SCC of size > 1, or contains a direct self-loop.

Related Issue

Closes #1628

@ThanosTsiamis ThanosTsiamis force-pushed the optimize-lakos-metrics branch from 1417c14 to 9de0a9a Compare May 25, 2026 11:05
Copy link
Copy Markdown
Member

@hankem hankem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this improvement!

On my system, it reduces the time to compute

ArchitectureMetrics.lakosMetrics(MetricsComponents.fromClasses(classes))

for hibernate-core:7.3.6 (with 37,281 classes)

  • from (101±2) s with ArchUnit 1.4.2
  • by a factor of more than 16 to (6.2±0.3) s. 🎉

@ThanosTsiamis ThanosTsiamis force-pushed the optimize-lakos-metrics branch from 038ce4f to 133c9d5 Compare May 25, 2026 19:38
Signed-off-by: Thanos Tsiamis <thatsiamis@gmail.com>

perf(metrics): PR review comments which optimizes imports

Signed-off-by: Thanos Tsiamis <thatsiamis@gmail.com>
@ThanosTsiamis ThanosTsiamis force-pushed the optimize-lakos-metrics branch from 133c9d5 to 5f22ee0 Compare May 26, 2026 07:29
@hankem hankem merged commit bd167e2 into TNG:main May 26, 2026
62 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(metrics): Optimize transitive reachability in LakosMetrics via SCC condensation and BitSet propagation

2 participants