访问 par_iter_mut 集合中不同索引的字段
Accessing fields of different index in collection inside par_iter_mut
以下示例说明了我正在尝试做的事情:
use rayon::prelude::*;
struct Parent {
children: Vec<Child>,
}
struct Child {
value: f64,
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Vec<f64>,
}
impl Parent {
fn calculate_distances(&mut self) {
self.children
.par_iter_mut()
.for_each(|x| x.calculate_distances(&self.children));
}
}
impl Child {
fn calculate_distances(&mut self, children: &[Child]) {
children
.iter()
.enumerate()
.for_each(|(i, x)| self.distances[i] = (self.value - x.value).abs());
}
}
以上无法编译。问题是,我无法在第一个 for_each 的闭包中访问 &self.children。我明白,为什么借用检查器不允许这样做,所以我的问题是,是否有一种方法可以让它在稍作改动的情况下工作。到目前为止我找到的解决方案并不真正令人满意。一种解决方案是在 Parent::calculate 距离的开头克隆子级并在闭包内使用它(这会添加一个不必要的克隆)。另一种解决方案是像这样提取 Child 的值字段:
use rayon::prelude::*;
struct Parent {
children: Vec<Child>,
values: Vec<f64>
}
struct Child {
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Vec<f64>,
}
impl Parent {
fn calculate_distances(&mut self) {
let values = &self.values;
self.children
.par_iter_mut()
.for_each(|x| x.calculate_distances(values));
}
}
impl Child {
fn calculate_distances(&mut self, values: &[f64]) {
for i in 0..values.len(){
self.distances[i]= (values[self.index]-values[i]).abs();
}
}
}
虽然这会很有效,但它完全弄乱了我的真实代码,并且值在概念上确实属于 Child。我对 Rust 比较陌生,只是问自己是否有什么好的方法可以做到这一点。据我所知,需要有一种方法来告诉编译器,我只更改并行迭代器中的距离字段,而值字段保持不变。也许这是一个使用不安全的地方?无论如何,如果您能在正确的方向上提示我,或者至少确认我的代码确实必须变得如此混乱才能使其正常工作,我将不胜感激:)
Rust 努力阻止你做你想做的事情:在修改它的同时保留对整个集合的访问。如果您不愿意调整数据布局以适应借用检查器,您可以使用内部可变性使 Child::calculate_distances
取 &self
而不是 &mut self
。然后你的问题就消失了,因为分发对 self.children
.
的多个共享引用是完全没问题的
理想情况下,您会使用 RefCell
,因为您不会从多个线程访问相同的 Child
。但 Rust 不允许这样做,因为根据所涉及函数的签名,您 可以 这样做,这将是一场数据竞赛。声明 distances: RefCell<Vec<f64>>
使 Child
不再 Sync
,删除对 Vec<Child>::par_iter()
.
的访问
你可以做的是使用 Mutex
。虽然一开始感觉很浪费,但请记住,每次调用 Child::calculate_distances()
都会收到不同的 Child
,因此互斥锁将始终处于无竞争状态,因此锁定成本较低(不涉及系统调用)。并且每个 Child::calculate_distances()
只锁定一次,而不是每次访问数组时。代码看起来像这样 (playground):
use rayon::prelude::*;
use std::sync::Mutex;
struct Parent {
children: Vec<Child>,
}
struct Child {
value: f64,
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Mutex<Vec<f64>>,
}
impl Parent {
fn calculate_distances(&mut self) {
self.children
.par_iter()
.for_each(|x| x.calculate_distances(&self.children));
}
}
impl Child {
fn calculate_distances(&self, children: &[Child]) {
let mut distances = self.distances.lock().unwrap();
children
.iter()
.enumerate()
.for_each(|(i, x)| distances[i] = (self.value - x.value).abs());
}
}
您也可以尝试将 std::sync::Mutex
替换为 parking_lot::Mutex
,后者更小(只有一个字节的开销,没有分配),更快,并且不需要 unwrap()
因为它不会锁中毒。
以下示例说明了我正在尝试做的事情:
use rayon::prelude::*;
struct Parent {
children: Vec<Child>,
}
struct Child {
value: f64,
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Vec<f64>,
}
impl Parent {
fn calculate_distances(&mut self) {
self.children
.par_iter_mut()
.for_each(|x| x.calculate_distances(&self.children));
}
}
impl Child {
fn calculate_distances(&mut self, children: &[Child]) {
children
.iter()
.enumerate()
.for_each(|(i, x)| self.distances[i] = (self.value - x.value).abs());
}
}
以上无法编译。问题是,我无法在第一个 for_each 的闭包中访问 &self.children。我明白,为什么借用检查器不允许这样做,所以我的问题是,是否有一种方法可以让它在稍作改动的情况下工作。到目前为止我找到的解决方案并不真正令人满意。一种解决方案是在 Parent::calculate 距离的开头克隆子级并在闭包内使用它(这会添加一个不必要的克隆)。另一种解决方案是像这样提取 Child 的值字段:
use rayon::prelude::*;
struct Parent {
children: Vec<Child>,
values: Vec<f64>
}
struct Child {
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Vec<f64>,
}
impl Parent {
fn calculate_distances(&mut self) {
let values = &self.values;
self.children
.par_iter_mut()
.for_each(|x| x.calculate_distances(values));
}
}
impl Child {
fn calculate_distances(&mut self, values: &[f64]) {
for i in 0..values.len(){
self.distances[i]= (values[self.index]-values[i]).abs();
}
}
}
虽然这会很有效,但它完全弄乱了我的真实代码,并且值在概念上确实属于 Child。我对 Rust 比较陌生,只是问自己是否有什么好的方法可以做到这一点。据我所知,需要有一种方法来告诉编译器,我只更改并行迭代器中的距离字段,而值字段保持不变。也许这是一个使用不安全的地方?无论如何,如果您能在正确的方向上提示我,或者至少确认我的代码确实必须变得如此混乱才能使其正常工作,我将不胜感激:)
Rust 努力阻止你做你想做的事情:在修改它的同时保留对整个集合的访问。如果您不愿意调整数据布局以适应借用检查器,您可以使用内部可变性使 Child::calculate_distances
取 &self
而不是 &mut self
。然后你的问题就消失了,因为分发对 self.children
.
理想情况下,您会使用 RefCell
,因为您不会从多个线程访问相同的 Child
。但 Rust 不允许这样做,因为根据所涉及函数的签名,您 可以 这样做,这将是一场数据竞赛。声明 distances: RefCell<Vec<f64>>
使 Child
不再 Sync
,删除对 Vec<Child>::par_iter()
.
你可以做的是使用 Mutex
。虽然一开始感觉很浪费,但请记住,每次调用 Child::calculate_distances()
都会收到不同的 Child
,因此互斥锁将始终处于无竞争状态,因此锁定成本较低(不涉及系统调用)。并且每个 Child::calculate_distances()
只锁定一次,而不是每次访问数组时。代码看起来像这样 (playground):
use rayon::prelude::*;
use std::sync::Mutex;
struct Parent {
children: Vec<Child>,
}
struct Child {
value: f64,
index: usize,
//will keep the distances to other children in the children vactor of parent
distances: Mutex<Vec<f64>>,
}
impl Parent {
fn calculate_distances(&mut self) {
self.children
.par_iter()
.for_each(|x| x.calculate_distances(&self.children));
}
}
impl Child {
fn calculate_distances(&self, children: &[Child]) {
let mut distances = self.distances.lock().unwrap();
children
.iter()
.enumerate()
.for_each(|(i, x)| distances[i] = (self.value - x.value).abs());
}
}
您也可以尝试将 std::sync::Mutex
替换为 parking_lot::Mutex
,后者更小(只有一个字节的开销,没有分配),更快,并且不需要 unwrap()
因为它不会锁中毒。