cch-rs
A practical, high-performance Customizable Contraction Hierarchy (CCH) implementation in Rust.
cch-rs is designed for real-world routing applications where graph topology can change, and edge weights (e.g., traffic data) update frequently. It provides a robust, stack-efficient architecture that integrates graph partitioning directly into the build process.
Key Features
- Batteries-Included Partitioning: Built-in METIS-based separator decomposition (via
metis-sys). No need for external graph processing tools or complex pre-processing pipelines. - Dynamic Topology: Supports both full graph rebuilding and incremental topology updates. Efficiently handle graph changes (adding nodes/edges) without complete re-computation.
- Stack Efficient: Unlike many recursive separator-based CCH implementations that require massive stack sizes for large graphs,
cch-rsis optimized for low stack overhead, making it stable on standard thread configurations. - High Availability & Concurrency: Built on
ArcSwap, ensuring wait-free reads during updates. You can safely run queries on one thread while updating weights or topology on another, with zero downtime and consistent snapshots. - Fast Customization: Update edge weights without rebuilding the contraction hierarchy structure.
- Incremental Weight Updates: Apply sparse
(edge_index, new_weight)changes withupdate_weights_partial, so single-edge or small-batch traffic changes do not require a full re-customization pass. - High Performance:
- Parallelized customization using
rayon. - Optimized memory layout for cache efficiency.
- Orders of magnitude faster than standard Dijkstra/A* for long-distance queries.
- Parallelized customization using
Installation
Add this to your Cargo.toml:
[]
= "0.2.0"
Note: This crate requires metis-sys, which depends on the METIS library.
Usage Example
use ;
use FxHashMap;
// 1. Define your graph (implementing the CchGraph trait)
Choosing an Update Path
- Use
update_weights(profile, new_weights)when most weights changed or you already have a full replacement vector. - Use
update_weights_partial(profile, updates)when only a small subset of original graph edges changed. updatesmust be expressed in the original graph's CSR edge order as(edge_index, new_weight).- Partial updates keep the same topology and only recompute affected hierarchy arcs and dependents.
Reusing a Partial-Update Context
If you are working directly with Cch and ProfileData instead of CchEngine, you can
cache the partial-update context and reuse it across many updates:
use ;
This avoids rebuilding the auxiliary reverse indices needed for incremental customization.
Performance and Testing
Benchmarks below were run on tests/data/USA-road-t.COL.gr + USA-road-d.COL.co
(435,666 nodes / 1,042,400 arcs).
| Operation | cch-rs |
RoutingKit |
Notes |
|---|---|---|---|
| Initialization | 1.3249 s | 2.4328 s | Includes ordering/build/first customization |
| Single-pair query with path extraction | 20.368 us | 52.285 us | cch-rs uses Criterion estimate, RoutingKit uses benchmark average |
| Full weight re-customization after one-edge change | 24.739 ms | 33.730 ms | Rebuilds metric/customization state after a weight change |
| Partial single-edge customization | 6.630 us | 8.960 us | Incremental update for one modified arc |
| Incremental topology update | 87.994 ms | N/A | Not provided by RoutingKit benchmark |
cch-rs numbers come from cargo bench --bench cch_bench -- --noplot using the
Criterion point estimate. RoutingKit numbers come from
.\benches\routingkit\build\routingkit_cch_bench.exe --gr tests\data\USA-road-t.COL.gr --co tests\data\USA-road-d.COL.co
using the reported RESULT averages.
License
Dual-licensed under MIT or Apache-2.0.