语境
我有一个情况,多个线程必须更新存储在共享向量中的对象。但是向量很大,要更新的元素数量比较少。
问题
在一个最小的示例中,要更新的元素集可以由包含要更新的元素的索引的(散列)集来标识。因此,代码可能如下所示:
let mut big_vector_of_elements = generate_data_vector();
while has_things_to_do() {
let indices_to_update = compute_indices();
indices_to_update.par_iter() // Rayon parallel iteration
.map(|index| big_vector_of_elements[index].mutate())
.collect()?;
}
这在 Rust 中显然是不允许的:big_vector_of_elements
不能同时在多个线程中可变地借用。但是,将每个元素包装在例如Mutex
锁中似乎是不必要的:这种特定情况在没有显式同步的情况下是安全的。由于索引来自一组,因此它们保证是不同的。没有两次迭代par_iter
接触向量的同一个元素。
重申我的问题
编写一个并行改变向量中元素的程序的最佳方法是什么,其中同步已经通过选择索引来处理,但是编译器不理解后者?
一个接近最佳的解决方案是将所有元素包装big_vector_of_elements
在某个假设的UncontendedMutex
锁中,这将是一种变体,Mutex
在无竞争的情况下速度快得离谱,并且在发生争用(甚至恐慌)时可能需要任意长的时间。理想情况下,对于 any , anUncontendedMutex<T>
也应该与 , 具有相同的大小和对齐方式。T
T
相关但不同的问题:
多个问题可以用“使用 Rayon 的并行迭代器”、“使用chunks_mut
”或“使用split_at_mut
”来回答:
- 如何在分区数组上运行并行计算线程?
- 并行处理 vec:如何安全地做,或者不使用不稳定的特性?
- 如何将不相交的切片从向量传递到不同的线程?
- 不同的线程可以写入同一个 Vec 的不同部分吗?
- 如何让每个 CPU 内核可变地访问 Vec 的一部分?
这些答案在这里似乎无关紧要,因为这些解决方案意味着迭代整个big_vector_of_elements
,然后为每个元素确定是否需要更改任何内容。从本质上讲,这意味着这样的解决方案如下所示:
let mut big_vector_of_elements = generate_data_vector();
while has_things_to_do() {
let indices_to_update = compute_indices();
for (index, mut element) in big_vector_of_elements.par_iter().enumerate() {
if indices_to_update.contains(index) {
element.mutate()?;
}
}
}
该解决方案所花费的时间与 的大小成正比big_vector_of_elements
,而第一个解决方案仅在与 的大小成比例的多个元素上循环indices_to_update
。