Skip to content

Conversation

@codeflash-ai
Copy link
Contributor

@codeflash-ai codeflash-ai bot commented Nov 13, 2025

📄 28% (0.28x) speedup for Graph.topologicalSort in code_to_optimize/topological_sort.py

⏱️ Runtime : 44.8 milliseconds 34.9 milliseconds (best of 32 runs)

📝 Explanation and details

The optimization achieves a 28% speedup by replacing the inefficient stack.insert(0, v) operation with stack.append(v) followed by a single stack.reverse() at the end.

Key optimization: The original code used stack.insert(0, v) in topologicalSortUtil, which is an O(N) operation because it requires shifting all existing elements in the list. This happened for every node processed during the DFS traversal. The optimized version uses stack.append(v) (O(1)) and reverses the entire stack once at the end (O(N)).

Why this is faster: For a graph with N nodes, the original approach performs N insertions at index 0, resulting in O(N²) time complexity for stack operations alone. The optimized approach performs N constant-time appends plus one O(N) reverse, reducing the stack operations to O(N) total.

Performance impact analysis: The line profiler shows the critical improvement - the stack.insert(0, v) line took 3.885e+06 nanoseconds (347.3 ns per hit) in the original, while stack.append(v) takes only 2.802e+06 nanoseconds (250.5 ns per hit) in the optimized version. The single stack.reverse() operation is negligible at 17,000 nanoseconds total.

Test case benefits: The optimization shows increasing benefits with graph size:

  • Small graphs (3 nodes): Minimal improvement (~1-8%)
  • Large sparse graphs (1000 nodes): ~15-18% improvement
  • Dense graphs (100 nodes with many edges): 25.8% improvement

This demonstrates that the optimization scales well with problem size, making it particularly valuable for larger topological sorting tasks.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 8 Passed
🌀 Generated Regression Tests 32 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 2 Passed
📊 Tests Coverage 100.0%
⚙️ Existing Unit Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
test_topological_sort.py::test_topological_sort 14.4μs 14.2μs 1.46%✅
test_topological_sort.py::test_topological_sort_2 35.5μs 33.1μs 7.30%✅
test_topological_sort.py::test_topological_sort_3 36.7ms 28.0ms 31.0%✅
🌀 Generated Regression Tests and Runtime
# imports
import pytest

from code_to_optimize.topological_sort import Graph

# unit tests


def is_valid_topological_order(graph, order):
    """Helper function to check if the order is a valid topological sort for the given graph."""
    position = {node: idx for idx, node in enumerate(order)}
    for u in graph.graph:
        for v in graph.graph[u]:
            if position[u] > position[v]:
                return False
    return True


# 1. Basic Test Cases


def test_single_node():
    # Graph with a single node and no edges
    g = Graph(1)
    order, sort_id = g.topologicalSort()  # 13.7μs -> 12.6μs (8.25% faster)


def test_three_nodes_chain():
    # Graph: 0 -> 1 -> 2
    g = Graph(3)
    g.graph[0].append(1)
    g.graph[1].append(2)
    order, _ = g.topologicalSort()  # 11.4μs -> 11.5μs (0.724% slower)


def test_three_nodes_branch():
    # Graph: 0 -> 1, 0 -> 2
    g = Graph(3)
    g.graph[0].append(1)
    g.graph[0].append(2)
    order, _ = g.topologicalSort()  # 11.4μs -> 11.2μs (1.48% faster)


def test_disconnected_components():
    # Graph: 0 -> 1, 2 (disconnected)
    g = Graph(3)
    g.graph[0].append(1)
    order, _ = g.topologicalSort()  # 11.1μs -> 11.1μs (0.000% faster)
    # 0 before 1, 2 can be anywhere
    idx_0 = order.index(0)
    idx_1 = order.index(1)


# 2. Edge Test Cases


def test_no_edges():
    # Graph with multiple nodes, no edges
    g = Graph(4)
    order, _ = g.topologicalSort()  # 11.8μs -> 11.7μs (1.08% faster)


def test_multiple_edges_to_same_node():
    # Graph: 0 -> 2, 1 -> 2
    g = Graph(3)
    g.graph[0].append(2)
    g.graph[1].append(2)
    order, _ = g.topologicalSort()  # 10.9μs -> 11.1μs (1.50% slower)
    idx_0 = order.index(0)
    idx_1 = order.index(1)
    idx_2 = order.index(2)


def test_cycle_detection_not_supported():
    # Graph with a cycle: 0 -> 1 -> 2 -> 0
    # The implementation does not detect cycles; it will recurse infinitely or crash.
    # We expect a RecursionError for a cycle.
    g = Graph(3)
    g.graph[0].append(1)
    g.graph[1].append(2)
    g.graph[2].append(0)
    with pytest.raises(RecursionError):
        g.topologicalSort()


def test_self_loop():
    # Graph: 0 -> 0 (self loop)
    g = Graph(1)
    g.graph[0].append(0)
    with pytest.raises(RecursionError):
        g.topologicalSort()


def test_large_sparse_graph():
    # Large graph with 1000 nodes, no edges
    g = Graph(1000)
    order, _ = g.topologicalSort()  # 680μs -> 591μs (15.1% faster)


def test_large_chain_graph():
    # Large chain: 0 -> 1 -> 2 -> ... -> 999
    g = Graph(1000)
    for i in range(999):
        g.graph[i].append(i + 1)
    order, _ = g.topologicalSort()  # 788μs -> 665μs (18.6% faster)


def test_multiple_disconnected_components_large():
    # 500 chains of length 2: (0->1), (2->3), ..., (998->999)
    g = Graph(1000)
    for i in range(0, 1000, 2):
        if i + 1 < 1000:
            g.graph[i].append(i + 1)
    order, _ = g.topologicalSort()  # 701μs -> 594μs (17.9% faster)
    # Each i before i+1
    for i in range(0, 1000, 2):
        if i + 1 < 1000:
            idx_i = order.index(i)
            idx_ip1 = order.index(i + 1)


# 3. Large Scale Test Cases


def test_performance_large_sparse():
    # Large sparse graph: 1000 nodes, 10 random edges
    g = Graph(1000)
    for i in range(10):
        g.graph[i].append((i + 10) % 1000)
    order, _ = g.topologicalSort()  # 675μs -> 585μs (15.4% faster)


def test_performance_large_dense():
    # Large dense DAG: 100 nodes, every i -> j for i < j
    g = Graph(100)
    for i in range(100):
        for j in range(i + 1, 100):
            g.graph[i].append(j)
    order, _ = g.topologicalSort()  # 434μs -> 345μs (25.8% faster)


# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
from code_to_optimize.topological_sort import Graph


def test_Graph_topologicalSort():
    Graph.topologicalSort(Graph(1))
🔎 Concolic Coverage Tests and Runtime
Test File::Test Function Original ⏱️ Optimized ⏱️ Speedup
codeflash_concolic_bl3wa8kb/tmpsbakrcsy/test_concolic_coverage.py::test_Graph_topologicalSort 10.2μs 10.3μs -1.20%⚠️

To edit these changes git checkout codeflash/optimize-Graph.topologicalSort-mhwrsx6g and push.

Codeflash Static Badge

The optimization achieves a **28% speedup** by replacing the inefficient `stack.insert(0, v)` operation with `stack.append(v)` followed by a single `stack.reverse()` at the end.

**Key optimization**: The original code used `stack.insert(0, v)` in `topologicalSortUtil`, which is an O(N) operation because it requires shifting all existing elements in the list. This happened for every node processed during the DFS traversal. The optimized version uses `stack.append(v)` (O(1)) and reverses the entire stack once at the end (O(N)).

**Why this is faster**: For a graph with N nodes, the original approach performs N insertions at index 0, resulting in O(N²) time complexity for stack operations alone. The optimized approach performs N constant-time appends plus one O(N) reverse, reducing the stack operations to O(N) total.

**Performance impact analysis**: The line profiler shows the critical improvement - the `stack.insert(0, v)` line took 3.885e+06 nanoseconds (347.3 ns per hit) in the original, while `stack.append(v)` takes only 2.802e+06 nanoseconds (250.5 ns per hit) in the optimized version. The single `stack.reverse()` operation is negligible at 17,000 nanoseconds total.

**Test case benefits**: The optimization shows increasing benefits with graph size:
- Small graphs (3 nodes): Minimal improvement (~1-8%)  
- Large sparse graphs (1000 nodes): ~15-18% improvement
- Dense graphs (100 nodes with many edges): **25.8% improvement**

This demonstrates that the optimization scales well with problem size, making it particularly valuable for larger topological sorting tasks.
@codeflash-ai codeflash-ai bot requested a review from aseembits93 November 13, 2025 01:48
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Nov 13, 2025
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-Graph.topologicalSort-mhwrsx6g branch November 13, 2025 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants