Skip to content

grt: FastRoute maze-routing performance (bit-identical)#10848

Open
saurav-fermions wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
Fermions-ASI:feat/grt-perf
Open

grt: FastRoute maze-routing performance (bit-identical)#10848
saurav-fermions wants to merge 2 commits into
The-OpenROAD-Project:masterfrom
Fermions-ASI:feat/grt-perf

Conversation

@saurav-fermions

Copy link
Copy Markdown
Contributor

Summary

Two constant-factor optimizations to the FastRoute global router. Global-route guide output is byte-for-byte identical before and after.

  1. Reuse 3D maze BFS scratch buffers — reuse two member scratch buffers (heapVisited, heapQueue) in addNeighborPoints instead of allocating net-node-sized local vectors on every call. Mirrors the existing maze2D visited_2D_/queue_2D_ reuse pattern.
  2. Hoist current-cell state in mazeRouteMSMDOrder3D — hoist the popped cell's distance / path-length / direction out of the boost multi_array 3D grids into locals, removing ~5 redundant 3D index computations per popped node.

Testing

Built openroad; full grt regression suite passes (140/140, incl. golden log_compare). Guide output bit-identical to baseline.

Notes

addNeighborPoints allocated two local vectors (heapVisited, heapQueue)
sized to the net node count on every call. On medium03 (98.6K cells)
that is ~175K calls and ~125M node-slots of alloc+init churn per
global_route, and the function is ~9% of the GRT profile.

Reuse two member scratch buffers instead, clearing/resizing the used
region each call rather than reallocating. This mirrors the existing
maze2D visited_2D_/queue_2D_ reuse pattern. heapVisited is reset with
assign(numNodes,false); heapQueue is write-before-read so only its size
matters.

Pure constant-factor change, no algorithmic difference: the global_route
guide output is byte-identical (same MD5) before and after, and the grt
regression suite stays 131/131. ~4.6% median global_route speedup on
medium03 (10385ms -> 9910ms, median of 3 runs each).


Signed-off-by: Saurav Singh <saurav.singh@fermions.co>
…bit-identical routing)

The 3D maze Dijkstra inner loop in mazeRouteMSMDOrder3D re-read the popped
("current") cell's distance, path length, and direction from the boost
multi_array 3D distance grids once per direction block -- up to six times per
popped node. These values are loop-invariant during a node's relaxation: only
the neighbor cells (tmpX/tmpY/tmpL) are written, never [curL][curY][curX]
itself. Each read pays a full boost::multi_array 3D index computation.

Hoist d1_3D_, path_len_3D_, and directions_3D_ for the current cell into locals
(cur_d1, cur_path_len, cur_dir) immediately after removeMin3D, and use them in
the six direction blocks. This removes ~5 redundant 3D index computations per
popped node. On aes_nangate45 the 3D maze pops ~22.4M nodes per global_route,
so the saved indexing is substantial.

Pure constant-factor change, no algorithmic difference. cur_d1 is int and is
promoted to float in `cur_d1 + cost + penalty` exactly as the original
multi_array read was, so arithmetic is identical.

Bit-identity proof (write_guides MD5, baseline bc114e8639 vs this change):
  gcd_nangate45 : da0e9f94e649c9f65443ce70d79ec7a9 (identical)
  aes_nangate45 : ccd6428a729cfea6c7228ac4d341c892 (identical, 156K cells)

Timing (global_route only, interleaved base vs opt, median of 6, aes 156K cells):
  base 4790 ms -> opt 4469 ms  (~6.7% faster, -321 ms)

grt regression: 131/131 pass (suite includes guideok comparisons across many
designs, an additional bit-identity check).


Signed-off-by: Saurav Singh <saurav.singh@fermions.co>
@saurav-fermions saurav-fermions requested review from a team as code owners July 9, 2026 06:55
@github-actions github-actions Bot added the size/S label Jul 9, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request optimizes 3D maze routing performance by reusing member scratch buffers (visited_3D_ and queue_3D_) to avoid per-call allocations, and caching current cell states to eliminate redundant 3D index computations. The reviewer suggests replacing std::vector<bool> with std::vector<char> for the scratch buffer to avoid bit-packing overhead and further improve performance.

// Reusable BFS scratch for addNeighborPoints (3D). Mirrors the maze2D
// visited_2D_/queue_2D_ reuse pattern: allocate once, reset the used region
// each call instead of reallocating per call.
std::vector<bool> visited_3D_;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using std::vector<bool> can be slower due to the overhead of bit-packing and bit-manipulation operations. Since this PR focuses on performance optimizations, using std::vector<char> (or std::vector<uint8_t>) instead of std::vector<bool> is a great opportunity to improve performance by allowing direct byte access.

Suggested change
std::vector<bool> visited_3D_;
std::vector<char> visited_3D_;

Comment on lines +107 to +110
visited_3D_.assign(numNodes, false);
queue_3D_.resize(numNodes);
std::vector<bool>& heapVisited = visited_3D_;
std::vector<int>& heapQueue = queue_3D_;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Update the type of heapVisited to std::vector<char>& to match the change of visited_3D_ from std::vector<bool> to std::vector<char> for better performance.

Suggested change
visited_3D_.assign(numNodes, false);
queue_3D_.resize(numNodes);
std::vector<bool>& heapVisited = visited_3D_;
std::vector<int>& heapQueue = queue_3D_;
visited_3D_.assign(numNodes, 0);
queue_3D_.resize(numNodes);
std::vector<char>& heapVisited = visited_3D_;
std::vector<int>& heapQueue = queue_3D_;

@osamahammad21 osamahammad21 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

revert the STA commit change. I assume this was accidental

@saurav-fermions

Copy link
Copy Markdown
Contributor Author

@osamahammad21 good catch — the src/sta change was accidental (a stray submodule-pointer bump). Removed and force-pushed; the PR is now src/sta-clean.

On the gemini suggestion to switch visited_3D_ from std::vector<bool> to std::vector<char>: I've kept std::vector<bool> deliberately, because it mirrors the existing maze2D visited_2D_ buffer (same type) that this change is modeled on. This PR is about reusing the scratch buffer to cut allocations, not changing its element type; routing output is bit-identical either way, and switching only the 3D buffer would make it inconsistent with the 2D one. Happy to do a separate vector<bool>→vector<char> sweep across both if the team prefers.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants