Supposing I have calculation like:
async fn sum_2(x: &f64) -> f64 {
x + 2
}
fn main() {
let mut x = vec![
vec![1.0, 2.0, 3.0],
vec![2.0, 3.0, 4.0],
];
for i in 0..2 {
for j in 0..3 {
x[i][j] = sum_2(x[i][j]).await;
}
}
}
What is the right/better way of performing this concurrently?
I am aware of join!(), which is excellent when the calls are known.
I also know the thread::spawn(), but I don't know if this is the right tool for achieving this, nor how. Isn't there something like a pool of threads in Rust that I could send all function calls into and then execute? Thanks!
Looks like you're looking for futures::future::join_all.
From the official example:
use futures::future::join_all;
async fn foo(i: u32) -> u32 { i }
let futures = vec![foo(1), foo(2), foo(3)];
assert_eq!(join_all(futures).await, [1, 2, 3]);
Related
I am trying to implement an outer function that could calculate the outer product of two 1D arrays. Something like this:
use std::thread;
use ndarray::prelude::*;
pub fn multithread_outer(A: &Array1<f64>, B: &Array1<f64>) -> Array2<f64> {
let mut result = Array2::<f64>::default((A.len(), B.len()));
let thread_num = 5;
let n = A.len() / thread_num;
// a & b are ArcArray2<f64>
let a = A.to_owned().into_shared();
let b = B.to_owned().into_shared();
for i in 0..thread_num{
let a = a.clone();
let b = b.clone();
thread::spawn(move || {
for j in i * n..(i + 1) * n {
for k in 0..b.len() {
// This is the line I want to change
result[[j, k]] = a[j] * b[k];
}
}
});
}
// Use join to make sure all threads finish here
// Not so related to this question, so I didn't put it here
result
}
You can see that by design, two threads will never write to the same element. However, rust compiler will not allow two mutable references to the same result variable. And using mutex will make this much slower. What is the right way to implement this function?
While it is possible to do manually (with thread::scope and split_at_mut, for example), ndarray already has parallel iteration integrated into its library, based on rayon:
https://docs.rs/ndarray/latest/ndarray/parallel
Here is how your code would look like with parallel iterators:
use ndarray::parallel::prelude::*;
use ndarray::prelude::*;
pub fn multithread_outer(a: &Array1<f64>, b: &Array1<f64>) -> Array2<f64> {
let mut result = Array2::<f64>::default((a.len(), b.len()));
result
.axis_iter_mut(Axis(0))
.into_par_iter()
.enumerate()
.for_each(|(row_id, mut row)| {
for (col_id, cell) in row.iter_mut().enumerate() {
*cell = a[row_id] * b[col_id];
}
});
result
}
fn main() {
let a = Array1::from_vec(vec![1., 2., 3.]);
let b = Array1::from_vec(vec![4., 5., 6., 7.]);
let c = multithread_outer(&a, &b);
println!("{}", c)
}
[[4, 5, 6, 7],
[8, 10, 12, 14],
[12, 15, 18, 21]]
I want a function that simply creates and returns a tuple as an example.
Basic template starting code (maturin new) looks like:
use pyo3::prelude::*;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
/// A Python module implemented in Rust.
#[pymodule]
fn tuple(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}
I have tried something like this but can't figure out what combination of things is required to even compile.
#[pyfunction]
fn f_ret_tuple() -> PyResult<&'static PyTuple> {
let gil = Python::acquire_gil();
let py = gil.python();
let tuple: &PyTuple;
let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
tuple = PyTuple::new(py, elements);
Ok(tuple)
}
The problem is that the compiler can't prove your tuple will not outlive the GIL acquisition. But per the docs,
In a function or method annotated with #[pyfunction] or #[pymethods] you can declare it as a parameter, and PyO3 will pass in the token when Python code calls it.
So:
#[pyfunction]
fn f_ret_tuple(py: Python<'_>) -> PyResult<&PyTuple> {
let tuple: &PyTuple;
let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
tuple = PyTuple::new(py, elements);
Ok(tuple)
}
I have a function that I would like to take an argument that can be looped over. However I would like to loop over it twice. I tried using the Iterator trait however I can only iterate over it once because it consumes the struct when iterating.
How should I make it so my function can loop twice? I know I could use values: Vec<usize> however I would like to make it generic over any object that is iterable.
Here's an example of what I would like to do: (Please ignore what the loops are actually doing. In my real code I can't condense the two loops into one.)
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize>,
{
// Loop one: This works.
let sum = values.sum::<usize>();
// Loop two: This doesn't work due to `error[E0382]: use of moved value:
// `values``.
let max = values.max().unwrap();
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
print!("Result: {}", result);
}
You can't iterate over the same iterator twice, because iterators are not guaranteed to be randomly accessible. For example, std::iter::from_fn produces an iterator that is most definitely not randomly accessible.
As #mousetail already mentioned, one way to get around this problem is to expect a Cloneable iterator:
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize> + Clone,
{
// Loop one: This works.
let sum = values.clone().sum::<usize>();
// Loop two: This doesn't work due to `error[E0382]: use of moved value:
// `values``.
let max = values.max().unwrap();
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
println!("Result: {}", result);
}
Result: 40
Although in your specific example, I'd compute both sum and max in the same iteration:
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize>,
{
let (sum, max) = values.fold((0, usize::MIN), |(sum, max), &el| {
(sum + el, usize::max(max, el))
});
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
println!("Result: {}", result);
}
Result: 40
I'd like to remove the use of .unwrap() from code which maps over an ndarray::Array and use a Result type for get_data() instead.
extern crate ndarray;
use ndarray::prelude::*;
use std::convert::TryFrom;
use std::error::Error;
fn get_data() -> Array2<usize> {
// In actual code, "a" comes from an external source, and the type
// is predetermined
let a: Array2<i32> = arr2(&[[1, 2, 3], [4, 5, 6]]);
let b: Array2<usize> = a.map(|x| usize::try_from(*x).unwrap());
b
}
fn main() -> Result<(), Box<dyn Error>> {
let a = get_data();
println!("{:?}", a);
Ok(())
}
For Vec, I've found this trick: How do I stop iteration and return an error when Iterator::map returns a Result::Err?.
However, this does not work with Arrays (collect isn't defined, and the semantics don't quite match up, since ndarray::Array defines a block of primitive types, which (AFAIU) can't hold Results).
Is there a nice way to handle this?
A native try_map implementation from ndarray would be ideal. It can short-circuit the computation and return as soon as an error occurs. It is also more composable.
Short of that, nothing wrong with a good old mutable sentinel variable:
extern crate ndarray;
use ndarray::prelude::*;
use std::convert::TryFrom;
use std::error::Error;
use std::num::TryFromIntError;
fn get_data() -> Result<Array2<usize>, TryFromIntError> {
let mut err = None;
let a: Array2<i32> = arr2(&[[1, 2, 3], [4, 5, 6]]);
let b: Array2<usize> = a.map(|&x| {
usize::try_from(x).unwrap_or_else(|e| {
err = Some(e);
Default::default()
})
});
err.map_or(Ok(b), Err)
}
fn main() -> Result<(), Box<dyn Error>> {
let a = get_data()?;
println!("{:?}", a);
Ok(())
}
I'm trying to parallelize an algorithm I have. This is a sketch of how I would write it in C++:
void thread_func(std::vector<int>& results, int threadid) {
results[threadid] = threadid;
}
std::vector<int> foo() {
std::vector<int> results(4);
for(int i = 0; i < 4; i++)
{
spawn_thread(thread_func, results, i);
}
join_threads();
return results;
}
The point here is that each thread has a reference to a shared, mutable object that it does not own. It seems like this is difficult to do in Rust. Should I try to cobble it together in terms of (and I'm guessing here) Mutex, Cell and &mut, or is there a better pattern I should follow?
The proper way is to use Arc<Mutex<...>> or, for example, Arc<RWLock<...>>. Arc is a shared ownership-based concurrency-safe pointer to immutable data, and Mutex/RWLock introduce synchronized internal mutability. Your code then would look like this:
use std::sync::{Arc, Mutex};
use std::thread;
fn thread_func(results: Arc<Mutex<Vec<i32>>>, thread_id: i32) {
let mut results = results.lock().unwrap();
results[thread_id as usize] = thread_id;
}
fn foo() -> Arc<Mutex<Vec<i32>>> {
let results = Arc::new(Mutex::new(vec![0; 4]));
let guards: Vec<_> = (0..4).map(|i| {
let results = results.clone();
thread::spawn(move || thread_func(results, i))
}).collect();
for guard in guards {
guard.join();
}
results
}
This unfortunately requires you to return Arc<Mutex<Vec<i32>>> from the function because there is no way to "unwrap" the value. An alternative is to clone the vector before returning.
However, using a crate like scoped_threadpool (whose approach could only be recently made sound; something like it will probably make into the standard library instead of the now deprecated thread::scoped() function, which is unsafe) it can be done in a much nicer way:
extern crate scoped_threadpool;
use scoped_threadpool::Pool;
fn thread_func(result: &mut i32, thread_id: i32) {
*result = thread_id;
}
fn foo() -> Vec<i32> {
let results = vec![0; 4];
let mut pool = Pool::new(4);
pool.scoped(|scope| {
for (i, e) in results.iter_mut().enumerate() {
scope.execute(move || thread_func(e, i as i32));
}
});
results
}
If your thread_func needs to access the whole vector, however, you can't get away without synchronization, so you would need a Mutex, and you would still get the unwrapping problem:
extern crate scoped_threadpool;
use std::sync::Mutex;
use scoped_threadpool::Pool;
fn thread_func(results: &Mutex<Vec<u32>>, thread_id: i32) {
let mut results = results.lock().unwrap();
result[thread_id as usize] = thread_id;
}
fn foo() -> Vec<i32> {
let results = Mutex::new(vec![0; 4]);
let mut pool = Pool::new(4);
pool.scoped(|scope| {
for i in 0..4 {
scope.execute(move || thread_func(&results, i));
}
});
results.lock().unwrap().clone()
}
But at least you don't need any Arcs here. Also execute() method is unsafe if you use stable compiler because it does not have a corresponding fix to make it safe. It is safe on all compiler versions greater than 1.4.0, according to its build script.