我的程序中有一些部分导入一个包含两个矩阵的文件并将它们相乘。
但我很困惑为什么并发的持续时间比没有并发的持续时间长?
我的代码中是否有任何错误?
// without concurrency
let mut result = vec![];
let time1 = Instant::now();
for i in 0..n {
let mut temp_vector = vec![];
for j in 0..n {
let mut temp_num = 0;
for multiple_count in 0..m {
temp_num = temp_num + arr1[i][multiple_count] * arr2[multiple_count][j];
}
temp_vector.push(temp_num);
}
result.push(temp_vector);
}
let time2 = Instant::now();
println!("normal solving result:\n");
for i in 0..n {
for j in 0..n {
print!("{:?} ", result[i][j]);
}
println!("");
}
let pass = time2.duration_since(time1);
println!("{:?}\n",pass);
println!("concurrency solving solution:\n");
// start the concurrency
let mut handles = vec![];
let arr1 = Arc::new(RwLock::new(arr1));
let arr2 = Arc::new(RwLock::new(arr2));
let count_time1 = Instant::now();
for i in 0..n {
for j in 0..n {
let arr1 = arr1.clone();
let arr2 = arr2.clone();
let handle = thread::spawn(move || {
let mut count = 0;
let arr1 = arr1.try_read().unwrap();
let arr2 = arr2.try_read().unwrap();
for k in 0..m {
count = count + arr1[i][k] * arr2[k][j];
}
count
});
handles.push(handle);
}
}
let count_time2 = Instant::now();
let pass_time = count_time2.duration_since(count_time1);
答案 0 :(得分:4)
有几个原因,但少数原因会跳出来:
你可能通过抛弃RWLock(因为你的数据是只读的,你不需要它)来获得相当大的加速,因为Arc只是延迟了启动时间和所需的时间要加入的线程(因为它需要删除Arc)。但是,到目前为止,你最大的加速只会产生4-8个线程,具体取决于你的处理器。我会告诉你如何最好地把它分成几块,但它相当简单。
编辑:事实上,你也可以摆脱Arc
,因为线程会立即加入,但是根据Rust线程的生命周期怪异,你可能需要crossbeam::scoped
来自crossbeam
的功能。 1}} crate实际上让它工作。
顺便说一句,一旦你转向并行写到同一个数据结构,我强烈建议你查看处理器缓存的信息,特别是虚假共享。虽然互斥量可能是Rust中较高的成本,但如果你能以某种方式避开它们(例如通过用split_mut
分割切片),你可能会因为不断地使边界周围的缓存无效而导致错误。
答案 1 :(得分:3)
不考虑太多细节:你正在产生n²个线程 - 结果矩阵中每个单元格一个。产生线程很昂贵(注意Rust不使用“绿色线程”,但默认使用系统线程)。
并发并不是简单地加快一切;一个人必须对此有点聪明。通常,您只想利用所有CPU内核,因此您应该只生成与内核一样多的线程。在你的情况下,产生线程可能比线程花费更多的时间,因此减速。