feat: Convert a subset of Mesa backend to Rust #2042
Replies: 3 comments 5 replies
-
Update: an apple to apple comparison of
I find "rust vector" syntax to be more expressive and pleasant than "cython vector": #[pyfunction]
fn get_neighborhood_vector(
pos: (i32, i32),
moore: bool,
include_center: bool,
radius: i32,
torus: bool,
width: i32,
height: i32,
) -> PyResult<Vec<(i32, i32)>> {
let (x, y) = pos;
// Estimate the maximum possible size of the neighborhood
let capacity = (2 * radius + 1).pow(2) as usize;
let mut neighborhood = Vec::with_capacity(capacity);
let check_bounds = |x: i32, y: i32| -> bool {
!(x < 0 || x >= width || y < 0 || y >= height)
};
for dx in -radius..=radius {
for dy in -radius..=radius {
if !moore && dx.abs() + dy.abs() > radius {
continue;
}
let mut new_x = x + dx;
let mut new_y = y + dy;
if torus {
new_x = ((new_x % width) + width) % width;
new_y = ((new_y % height) + height) % height;
} else if !check_bounds(new_x, new_y) {
continue;
}
if !(!include_center && dx == 0 && dy == 0) {
neighborhood.push((new_x, new_y));
}
}
}
Ok(neighborhood)
} vs Cython @cython.boundscheck(False)
@cython.wraparound(False)
cpdef list get_neighborhood(
self,
object pos,
bint moore,
bint include_center,
int radius,
bint torus,
):
cdef int x, y
x, y = pos
cdef int max_neighborhood_count
max_neighborhood_count = (2 * radius + 1) ** 2
cdef long[:, :] neighborhood
neighborhood = np.empty((max_neighborhood_count, 2), long)
cdef int count
count = 0
cdef int new_x, new_y
cdef int dx, dy
for dx in range(-radius, radius + 1):
for dy in range(-radius, radius + 1):
if not moore and abs(dx) + abs(dy) > radius:
continue
new_x = x + dx
new_y = y + dy
if torus:
new_x %= self.width
new_y %= self.height
elif self.out_of_bounds((new_x, new_y)):
continue
if not (not include_center and dx == 0 and dy == 0):
neighborhood[count, 0] = new_x
neighborhood[count, 1] = new_y
count += 1
# Convert to list
cdef list neighborhood_list
neighborhood_list = [0] * count
for i in range(count):
neighborhood_list[i] = (neighborhood[i, 0], neighborhood[i, 1])
return neighborhood_list Where in Rust:
Note: It's not fully apple to apple comparison either, because the Cython version cheats by not doing bounds checking. @quaquel what do you think of the syntax so far? Or would you delay judgement until I translate |
Beta Was this translation helpful? Give feedback.
-
Here is a benchmark result of
The current Rust version is not as fast as |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Git branch: https://github.com/rht/mesa/tree/rust
This is an experiment in using a Rust backend for Mesa. The tooling is very straightforward. Initial implementation of
get_neighborhood2
shows that it is about 2x faster than pure Python, while a Cython versionwith a NumPy grid(the grid data type does not matter forget_neighborhood
) is about 7x faster than pure Python. According to ChatGPT, there are still lots of room for improvement.Benchmark parameter
Measured using https://github.com/rht/mesa-perf/blob/main/experiments/get_neighborhood/benchmark.py.
To quickly test this branch, you need:
rustc
maturin
(viapip install maturin
; haven't testeduv pip install
yet)maturin develop
, alternatively, runmaturin develop --release
for faster codeBeta Was this translation helpful? Give feedback.
All reactions