Skip to content

Conversation

@aiAdrian
Copy link
Contributor

@aiAdrian aiAdrian commented Oct 16, 2025

Description

It would be highly beneficial to process this in parallel using 2 to 4 threads.
By applying Promise-based chunking and await logic, we could significantly reduce total runtime.
Especially for large network graphs, sequential pathfinding can become a major bottleneck.

Currently, pathfinding runs sequentially for each origin node and for big netzgrafik it takes quite a lot of time!

   // In theory we could parallelize the pathfindings, but the overhead might be too big.
    const res = new Map<string, [number, number]>();
    odNodes.forEach((origin) => {
      computeShortestPaths(origin.getId(), neighbors, vertices, tsSuccessor, cachedKey).forEach(
        (value, key) => {
          res.set([origin.getId(), key].join(","), value);
        },
      );
    });

If we can make the computeShortestPaths as well async then the following code should be executable in parallel. You can just add a new method computeBatchShortestPaths to OriginDestinationService :

async computeBatchShortestPaths(
    odNodes: Node[],
    neighbors: Map<string, [Vertex, number][]>,
    vertices: Vertex[],
    tsSuccessor: Map<number, number>,
    cachedKey: Map<Vertex, string>,
    res: Map<string, [number, number]>,
  ): Promise<void> {
    const numThreads = 1;
    const chunkSize = Math.ceil(odNodes.length / numThreads);
    const allChunks: [number, number][] = [];

    for (let i = 0; i < odNodes.length; i += chunkSize) {
      allChunks.push([i, Math.min(i + chunkSize, odNodes.length)]);
    }

    const threads: Promise<void>[] = [];

    for (const [start, end] of allChunks) {
      const chunk = odNodes.slice(start, end);

      const thread = new Promise<void>((resolve) => {
        for (const origin of chunk) {
          const results = computeShortestPaths(
            origin.getId(),
            neighbors,
            vertices,
            tsSuccessor,
            cachedKey,
          );

          results.forEach((value, key) => {
            res.set([origin.getId(), key].join(","), value);
          });
        }

        console.log("done:", start, end);
        resolve();
      });

      threads.push(thread);
    }

    await Promise.all(threads);
  }

In the originDestinationData(): OriginDestination[] { ... } function (method) we have to replace:

This code snipped

    odNodes.forEach((origin) => {
      computeShortestPaths(origin.getId(), neighbors, vertices, tsSuccessor, cachedKey).forEach(
        (value, key) => {
          res.set([origin.getId(), key].join(","), value);
        },
      );
    });

with

   this.computeBatchShortestPaths(odNodes, neighbors, vertices, tsSuccessor, cachedKey, res);

Issues

Checklist

  • This PR contains a description of the changes I'm making
  • I've read the Contribution Guidelines
  • I've added tests for changes or features I've introduced
  • I documented any high-level concepts I'm introducing in documentation/
  • CI is currently green and this is ready for review

@aiAdrian aiAdrian self-assigned this Oct 16, 2025
@aiAdrian aiAdrian marked this pull request as draft October 16, 2025 20:31
@aiAdrian aiAdrian mentioned this pull request Oct 16, 2025
5 tasks

// Given a graph (adjacency list), and vertices in topological order, return the shortest paths (and connections)
// from a given node to other nodes.
async computeShortestPaths(
Copy link
Contributor Author

@aiAdrian aiAdrian Oct 16, 2025

Choose a reason for hiding this comment

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

Just a copy of the original code but declared as async. - only for quick & dirty testing

// The shortest path from the start node to this vertex is a shortest path from the start node to a neighbor
// plus the weight of the edge connecting the neighbor to this vertex.
//neighs.forEach(([neighbor, weight]) => {
for (let idx = 0; idx < neighs.length; idx++) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

forEach replaced

@aiAdrian
Copy link
Contributor Author

aiAdrian commented Oct 16, 2025

Branch Performance Comparison

Browser Branch: main Branch: aeg/performance_computeShortestPaths Branch: aeg/performance_computeShortestPaths_parallel_preparation
Google Chrome 541 ms 169 ms 171 ms
Microsoft Edge 879 ms 420 ms 320 ms

Conclusion

Ultimately, this change doesn’t have a significant impact.

  • Switching from main to aeg/performance_computeShortestPaths results in a 2–3x speed improvement.'
  • From aeg/performance_computeShortestPaths to aeg/performance_computeShortestPaths_parallel_preparation there is no real win but the added complexity in the code isn’t justified by the actual performance gain.

We might need to explore real multithreading (Node.js worker threads could offer more power,
but again, they introduce much more complexity).

Suggestion: Reject this approach for now.

@shenriotpro
Copy link
Contributor

Thanks for trying :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants