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.
-
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