Skip to content

Reduce allocation overhead in dag_algo::longest_path#1563

Open
mtreinish wants to merge 1 commit intoQiskit:mainfrom
mtreinish:limit-allocation-longest-path
Open

Reduce allocation overhead in dag_algo::longest_path#1563
mtreinish wants to merge 1 commit intoQiskit:mainfrom
mtreinish:limit-allocation-longest-path

Conversation

@mtreinish
Copy link
Member

This commit reduces the overhead from allocations in the longest_path function. Previously for each node being iterated over we were allocating a new vec and growing it as we pushed. This results in a lot of allocations and frees as we iterate over all the nodes. This commit address this overhead by moving to a single vec outside the loop and just clearing it on every iteration. This will reuse the allocation and minimize the overhead this causes.

This commit reduces the overhead from allocations in the longest_path
function. Previously for each node being iterated over we were
allocating a new vec and growing it as we pushed. This results in a lot
of allocations and frees as we iterate over all the nodes. This commit
address this overhead by moving to a single vec outside the loop
and just clearing it on every iteration. This will reuse the allocation
and minimize the overhead this causes.
Comment on lines 305 to 321
@@ -313,8 +314,9 @@ where
}
// Determine the maximum distance and corresponding parent node
let max_path: (T, G::NodeId) = incoming_path
.into_iter()
.iter()
.max_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
.copied()
.unwrap_or((T::zero(), node)); // If there are no incoming edges, the distance is zero

Copy link
Member

Choose a reason for hiding this comment

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

I don't even think you need a Vec at all, no?

Something like

for node in nodes {
    let max_path = graph
        .edges_directed(node, Incoming)
        .try_fold((T::zero(), node), |((max_length, prev), edge)| -> Result<_, E> {
            let source = edge.source();
            let length = dist[&source].0 + weight_fn(edge)?;
            if length > max_length {
                Ok((length, source))
            else {
                Ok((max_length, prev))
            }
        })?;
    // ...

@jakelishman
Copy link
Member

Should also be noted that, at least on Qiskit, we don't actually care what the longest path is, just what its cost is, so when we call this function, we're wasting time by backtracking through the hashmap and allocating a path just to throw it away again.

@mtreinish
Copy link
Member Author

Should also be noted that, at least on Qiskit, we don't actually care what the longest path is, just what its cost is, so when we call this function, we're wasting time by backtracking through the hashmap and allocating a path just to throw it away again.

It would be simple enough to add a longest_path_length function to return just the length and skip the unnecessary overhead for Qiskit's use case.

Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

btw I'm fine with the change as-is, whether or not you want to refactor to avoid ever having a Vec

@IvanIsCoding IvanIsCoding mentioned this pull request Mar 14, 2026
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.

2 participants