I am trying to implement a reasonably fast version of Floyd-Warshall algorithm in Rust. This algorithm finds a shortest paths between all vertices in a directed weighted graph.
The main part of the algorithm could be written like this:
// dist[i][j] contains edge length between vertices [i] and [j]
// after the end of the execution it contains shortest path between [i] and [j]
fn floyd_warshall(dist: &mut [Vec<i32>]) {
let n = dist.len();
for i in 0..n {
for j in 0..n {
for k in 0..n {
dist[j][k] = min(dist[j][k], dist[j][i] + dist[i][k]);
}
}
}
}
This implementation is very short and easy to understand, but it works 1.5x slower than similar c++ implementation.
As I understand problem is that on each vector access Rust checks that index is inside bounds of the vector, and it adds some overhead.
I rewrote this function with get_unchecked* functions:
fn floyd_warshall_unsafe(dist: &mut [Vec<i32>]) {
let n = dist.len();
for i in 0..n {
for j in 0..n {
for k in 0..n {
unsafe {
*dist[j].get_unchecked_mut(k) = min(
*dist[j].get_unchecked(k),
dist[j].get_unchecked(i) + dist[i].get_unchecked(k),
)
}
}
}
}
}
and it really started working 1.5x faster (full code of the test).
I didn't expect bounds check to add that much overhead :(
Is it possible to rewrite this code in an idiomatic way without unsafes, such that it works as fast as unsafe version? E.g. is it possible to "prove" to the compiler that there will be no out of bound access by adding some assertions to the code?