【问题标题】:Parallel write to array with an indices array [duplicate]使用索引数组并行写入数组[重复]
【发布时间】:2020-10-19 07:41:28
【问题描述】:

我无法理解来自 C++ 的 Rust 并发模型。

将使用另一个定义索引的数组同时访问我的数组。例如(伪代码):

let indices = [1, 2, 3, 4, 1, 2, 3, 2, 1, 1, 3, 2, 2];
let mut arr = [1, 2, 3, 4, 5, 6, 7, 8, 10];

indices.iter_par().for_each(|x| {
    arr[x] += x;
});

在 C++ 中,我会使用锁或使用原子访问来保护arr 中的每个索引。我怎么能在 Rust 中做同样的事情?

编辑

我还有一个相关的问题。

我如何将一个普通数组作为可变数组传递给并行迭代器,我确定不会出现竞争条件?

let indices = [1, 2, 3, 4, 5, 6, 7, 8];
let mut arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

indices.iter_par().for_each(|x| {
    arr[x] = some_function(x);
});

【问题讨论】:

  • “在 C++ 中,我会简单地用锁保护 arr 中的每个索引或使用原子访问。我如何在 rust 中做同样的事情?” - 你也可以这样做在 Rust 中使用 Mutex<i32>s 或 AtomicI32s 数组(如果需要)
  • 我遇到的问题是 rust 中的互斥锁的功能。与 C++ 不同,rust mutex 封装了它保护的数据。我需要对 arr 进行单独保护以进行并行访问或类似的东西。使整个数组原子化是一种可能性,但不是首选。
  • @Adam 不,互斥体和原子提供了内部可变性,因此您根本不需要保护数组本身:Rust 的借用检查器不会让您同时读取数组(访问或更新其项目)并更新它(添加新项目),因此它充当“静态”RWLock。虽然如果您需要更复杂的东西,您当然可以将数组包装在 RWLock 中,在运行时工作。
  • 虽然请注意,预处理indices 以计算每个索引处的增量数可能是一个更好的主意,然后您可以将其压缩到您的数组并使用类似par_iter_mut为了增加每个索引正确的次数,而不需要在您自己的代码中进行任何同步。

标签: arrays concurrency rust parallel.foreach


【解决方案1】:

如果您需要为每个项目锁定,我不知道并行执行此操作的意义是什么,但是您可以使用数组周围的Mutex 来实现这一点以进行变异:

use rayon::prelude::*;
use std::sync::Mutex;

fn main() {
    let indices = [1, 2, 3, 4, 1, 2, 3, 2, 1, 1, 3, 2, 2];
    let arr = Mutex::new([1, 2, 3, 4, 5, 6, 7, 8, 10]);

    indices.par_iter().for_each(|&x| {
        let mut arr = arr.lock().unwrap();
        arr[x] += x;
    });
}

playground

编辑

根据注释,您可以让每个元素都是原子的:

use rayon::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};

fn main() {
    let indices = [1, 2, 3, 4, 1, 2, 3, 2, 1, 1, 3, 2, 2];
    let arr = [1, 2, 3, 4, 5, 6, 7, 8, 10]
        .iter()
        .map(|&n| AtomicUsize::new(n))
        .collect::<Vec<_>>();

    indices.par_iter().for_each(|&x| {
        arr[x].fetch_add(x, Ordering::SeqCst);
    });
}

【讨论】:

  • 谢谢!,这是一种可能的解决方案。有没有另一种使用锁数组之类的方法?
  • 是的,就像我将每个数组元素映射到 AtomicUsize 一样,您可以对 Mutex 执行相同操作,然后在循环内您可以锁定单个元素:*arr[x].lock().unwrap() += x
  • 如果你喜欢危险的生活,你可以type-pun that array with a slice保存map+collect。这在AtomicUsizeusize 对齐更严格的平台上不合理(我不知道有任何这样的平台,但它们可能存在)。这也很容易搞砸,因为指针转换不会告诉您类型是否被推断错误(例如,如果您删除 : [usize; 9],则会得到 UB)。总的来说,这是一个坏主意,我不知道我为什么建议这样做。
  • (但是,鉴于Atomic*::from_mut just got merged (again),也许我们可以希望未来有一个AtomicUsize::from_mut_slice 可以避免这些陷阱并让您以完全安全的代码执行零拷贝版本。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
  • 2017-09-13
  • 1970-01-01
  • 2018-08-06
  • 1970-01-01
相关资源
最近更新 更多