我正在尝试通过制作一个简单的向量加法函数来学习 Rust 的 Rayon 库。我当前的代码是这样的,假设
a
、b
和 c
是初始化为相同长度的向量,c
是可变的,num_threads
是一个 usize
变量:
let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()
.expect("Could not create thread pool");
pool.install(|| {
(0..c.len()).into_par_iter().for_each(|x| {
c[x] = a[x] + b[x];
});
});
但我收到错误
error[E0596]: cannot borrow c as mutable, as it is a captured variable in a Fn closure
c[x] = a[x].wrap_add(b[x]);
^ cannot borrow as mutable
我还想指出,最终我的目标是制作基准测试软件,这就是为什么我使用线程池来指定线程数,但我认为
pool.install()
使用的额外闭包是问题根源的一部分。修改全局线程池也不是一个选项,因为只能完成一次,而且我想使用不同的线程数重新运行基准测试。我还想避免使用作用域 - 如果这是唯一的解决方案,那就这样吧 - 因为这会增加性能损失。
我从根本上理解 Rayon 在这里不喜欢什么:Rust Book 的第 4.2 章说你不能对一个变量有多个可变引用,这本质上是每个线程都会得到的。然而,除了如何让这段代码运行的特定问题之外,这还引发了一些其他问题。
我似乎甚至无法将对
c
的引用移至线程池闭包中。为什么这是一个限制?当然,多线程的强大之处在于让多个线程同时对相关数据进行一些工作,那么为什么我不能以这种方式将数据传递给线程呢?
假设我可以将
c
的引用放入线程池中,是否有某种方法可以实现,例如,线程 0 获取 &mut c[0]
,线程 1 获取 &mut c[1]
,依此类推?如果 Rayon 的目的是抽象 Rust 基本多线程库的一些样板,那么 Rayon 不应该尝试让这变得更简单吗?
我看到的其他一些答案暗示迭代向量本身的内容会有所帮助,但由于我需要所有三个向量,所以我需要使用izip。这样做(用
(0..c.len().into_par_iter()...
替换 izip!(&a.mat, &b.mat, &mut c.mat).into_par_iter()...
)给了我错误
error[E0599]: the method into_par_iter exists for struct Map<Zip<Zip<Iter<'_, T>, Iter<'_, T>>, IterMut<'_, T>>, {[email protected]:303:9}>, but its trait bounds were not satisfied
izip!(&a.mat, &b.mat, &mut c.mat).into_par_iter().for_each(|x| {
| ^^^^^^^^^^^^^
|
= note: the following trait bounds were not satisfied:
`std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
which is required by `std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`
`&std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
which is required by `&std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`
`&mut std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::ParallelIterator`
which is required by `&mut std::iter::Map<std::iter::Zip<std::iter::Zip<std::slice::Iter<'_, T>, std::slice::Iter<'_, T>>, std::slice::IterMut<'_, T>>, {closure@/home/richard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.12.1/src/lib.rs:303:9: 303:10}>: rayon::iter::IntoParallelIterator`
这似乎意味着引用现在已移至闭包中。是什么赋予了?为什么之前没有被移动?
我已经找到解决办法了。我需要通过在线程池的闭包中使用迭代器来更明确地借用
c
- 这似乎也解释了我的问题的“假设我可以......”部分 - 以及 @user2407038 的建议平行拉链:
let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()
.expect("Could not create thread pool");
pool.install(|| {
(&mut c).into_par_iter().zip(0..c.len()).for_each(|x| {
*x.0 = a[x.1] + b[x.1];
});
});