Multithreading 人造丝中的每线程初始化
我正在尝试使用人造丝的Multithreading 人造丝中的每线程初始化,multithreading,rust,rayon,Multithreading,Rust,Rayon,我正在尝试使用人造丝的par_iter()优化我的功能 单线程版本类似于: fn verify_and_store(store: &mut Store, txs: Vec<Tx>) { let result = txs.iter().map(|tx| { tx.verify_and_store(store) }).collect(); ... } 但是,这会在每次迭代时克隆存储,这太慢了。我希望每个线程使用一个存储实例 人造
par_iter()
优化我的功能
单线程版本类似于:
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
let result = txs.iter().map(|tx| {
tx.verify_and_store(store)
}).collect();
...
}
但是,这会在每次迭代时克隆存储
,这太慢了。我希望每个线程使用一个存储实例
人造丝可以吗?或者我应该求助于手动线程和工作队列吗?可以使用线程局部变量来确保在给定线程中创建的
local\u store
不会超过一次
例如,它编译():
老问题,但我觉得答案需要重新审视。一般来说,有两种方法: 与一起使用
map\u。每当一个线程从另一个线程窃取一个工作项时,就会进行克隆。这可能会克隆比线程多的存储,但应该相当低。如果克隆成本太高,您可以使用和
增加rayon将拆分工作负载的大小
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
let result = txs.iter().map_with(|| store.clone(), |store, tx| {
tx.verify_and_store(store)
}).collect();
...
}
很遗憾,这个调用似乎没有任何作用域(尽管这在相当多的情况下显然是有用的)。@ChrisEmerson是的,这个答案让我担心的是,我想不出一种方法来使用安全代码清理创建的存储(或者在一切完成后运行其他任意命令,例如将它们刷新到磁盘)。更糟糕的是,下一次调用verify_and_store
将继续使用最后一个已知的store
克隆,这些克隆可能与当前的store
无关。谢谢。这是可行的,但在我的特殊情况下,我发现人造丝有par_块
来减少克隆的数量。虽然这可能仍然会导致每个线程有多个克隆,但它不存在@user4815162342描述的范围问题。@ChrisEmerson我现在已经更新了答案,以添加范围和适当的清理;代码并不优雅(至少可以这么说),但它似乎可以工作@Tomas您可能想添加您的解决方案作为答案,并接受它。我的答案是有趣地探索Rust/Rayon对线程本地代码的支持,而您的par_chunks
解决方案似乎在解决实际问题方面做得很好。
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
use std::cell::RefCell;
thread_local!(static STORE: RefCell<Option<Store>> = RefCell::new(None));
let mut result = Vec::new();
txs.par_iter().map(|tx| {
STORE.with(|cell| {
let mut local_store = cell.borrow_mut();
if local_store.is_none() {
*local_store = Some(store.clone());
}
tx.verify_and_store(local_store.as_mut().unwrap())
})
}).collect_into(&mut result);
}
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
use std::sync::{Arc, Mutex};
type SharedStore = Arc<Mutex<Option<Store>>>;
lazy_static! {
static ref STORE_CLONES: Mutex<Vec<SharedStore>> = Mutex::new(Vec::new());
static ref NO_REENTRY: Mutex<()> = Mutex::new(());
}
thread_local!(static STORE: SharedStore = Arc::new(Mutex::new(None)));
let mut result = Vec::new();
let _no_reentry = NO_REENTRY.lock();
txs.par_iter().map({
|tx| {
STORE.with(|arc_mtx| {
let mut local_store = arc_mtx.lock().unwrap();
if local_store.is_none() {
*local_store = Some(store.clone());
STORE_CLONES.lock().unwrap().push(arc_mtx.clone());
}
tx.verify_and_store(local_store.as_mut().unwrap())
})
}
}).collect_into(&mut result);
let mut store_clones = STORE_CLONES.lock().unwrap();
for store in store_clones.drain(..) {
store.lock().unwrap().take();
}
}
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
let result = txs.iter().map_with(|| store.clone(), |store, tx| {
tx.verify_and_store(store)
}).collect();
...
}
fn verify_and_store(store: &mut Store, txs: Vec<Tx>) {
let tl = ThreadLocal::new();
let result = txs.iter().map(|tx| {
let store = tl.get_or(|| Box::new(RefCell::new(store.clone)));
tx.verify_and_store(store.get_mut());
}).collect();
...
}