Skip to content

perf: avoid redundant work in Graph.transitive_closure - #1015

Open
hneiva wants to merge 1 commit into
mainfrom
hneiva/transitive-closure2
Open

perf: avoid redundant work in Graph.transitive_closure#1015
hneiva wants to merge 1 commit into
mainfrom
hneiva/transitive-closure2

Conversation

@hneiva

@hneiva hneiva commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

The adjacency map is built over every edge before the traversal starts, which an empty node set never needs. Return early instead: on a 20k-node, 2M-edge graph an empty closure drops from ~1.3s to microseconds.

Resolve reverse to a pair of tuple indices once rather than branching on it per edge in both loops, worth 1.1-1.4x on sparse graphs. Accept a frozenset in the input assert, since Graph.nodes is itself a frozenset and g.transitive_closure(g.nodes) previously tripped it.

The four worked examples in the docstring had forward and reverse swapped. They have been wrong since 2eec861 imported the module in 2019, were never covered by a test, and contradict the prose above them, which was correct.

Add coverage for the diamond, frozenset and unknown-node paths.

The adjacency map is built over every edge before the traversal starts,
which an empty node set never needs. Return early instead: on a
20k-node, 2M-edge graph an empty closure drops from ~1.3s to
microseconds.

Resolve `reverse` to a pair of tuple indices once rather than branching
on it per edge in both loops, worth 1.1-1.4x on sparse graphs. Accept a
frozenset in the input assert, since Graph.nodes is itself a frozenset
and g.transitive_closure(g.nodes) previously tripped it.

The four worked examples in the docstring had forward and reverse
swapped. They have been wrong since 2eec861 imported the module in
2019, were never covered by a test, and contradict the prose above them,
which was correct.

Add coverage for the diamond, frozenset and unknown-node paths.
@codspeed-hq

codspeed-hq Bot commented Jul 29, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 36.14%

⚡ 1 improved benchmark
✅ 23 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
test_transitive_closure[btree] 26.3 ms 19.3 ms +36.14%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing hneiva/transitive-closure2 (60ee181) with main (8169bc2)

Open in CodSpeed

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.50%. Comparing base (8169bc2) to head (60ee181).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1015      +/-   ##
==========================================
+ Coverage   77.47%   77.50%   +0.03%     
==========================================
  Files         132      132              
  Lines       12552    12561       +9     
  Branches     1506     1507       +1     
==========================================
+ Hits         9725     9736      +11     
+ Misses       2439     2438       -1     
+ Partials      388      387       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hneiva
hneiva marked this pull request as ready for review July 30, 2026 14:50
@hneiva
hneiva requested a review from a team as a code owner July 30, 2026 14:50
@hneiva
hneiva requested a review from ahal July 30, 2026 14:50
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.

1 participant