gpt4 book ai didi

multithreading - 为什么使用 Iterator::map 生成线程不能并行运行线程?

转载 作者:行者123 更新时间:2023-11-29 08:23:16 24 4
gpt4 key购买 nike

我用 Rust 写了一个简单的多线程应用程序来添加从 1 到 x 的数字。 (我知道有一个公式,但重点是用 Rust 编写一些多线程代码,而不是得到结果。)它运行良好,但在我将其重构为更具功能性的样式而不是命令式之后,多线程不再有任何加速。检查 CPU 使用率时,我的 4 核/8 线程 CPU 似乎只使用了一个核。原始代码有790%的CPU使用率,而重构后的版本只有99%。

原代码:

use std::thread;

fn main() {
let mut handles: Vec<thread::JoinHandle<u64>> = Vec::with_capacity(8);

const thread_count: u64 = 8;
const batch_size: u64 = 20000000;

for thread_id in 0..thread_count {
handles.push(thread::spawn(move || {
let mut sum = 0_u64;

for i in thread_id * batch_size + 1_u64..(thread_id + 1) * batch_size + 1_u64 {
sum += i;
}

sum
}));
}

let mut total_sum = 0_u64;

for handle in handles.into_iter() {
total_sum += handle.join().unwrap();
}
println!("{}", total_sum);
}

重构后的代码:

use std::thread;

fn main() {
const THREAD_COUNT: u64 = 8;
const BATCH_SIZE: u64 = 20000000;

// spawn threads that calculate a part of the sum
let handles = (0..THREAD_COUNT).map(|thread_id| {
thread::spawn(move ||
// calculate the sum of all numbers from assigned to this thread
(thread_id * BATCH_SIZE + 1 .. (thread_id + 1) * BATCH_SIZE + 1)
.fold(0_u64,|sum, number| sum + number))
});

// add the parts of the sum together to get the total sum
let sum = handles.fold(0_u64, |sum, handle| sum + handle.join().unwrap());

println!("{}", sum);
}

程序的输出相同(12800000080000000),但重构版本慢了 5-6 倍。

迭代器似乎是惰性求值的。如何强制评估整个迭代器?我试图将它收集到类型为 [thread::JoinHandle<u64>; THREAD_COUNT as usize] 的数组中,但随后出现以下错误:

  --> src/main.rs:14:7
|
14 | ).collect::<[thread::JoinHandle<u64>; THREAD_COUNT as usize]>();
| ^^^^^^^ a collection of type `[std::thread::JoinHandle<u64>; 8]` cannot be built from `std::iter::Iterator<Item=std::thread::JoinHandle<u64>>`
|
= help: the trait `std::iter::FromIterator<std::thread::JoinHandle<u64>>` is not implemented for `[std::thread::JoinHandle<u64>; 8]`

收集到向量中确实有效,但这似乎是一个奇怪的解决方案,因为大小是预先知道的。有没有比使用向量更好的方法?

最佳答案

Rust 中的迭代器是惰性的,因此直到 handles.fold 尝试访问迭代器的相应元素时,您的线程才会启动。基本上发生的事情是:

  1. handles.fold 尝试访问迭代器的第一个元素。
  2. 第一个线程启动。
  3. handles.fold 调用其闭包,后者为第一个线程调用 handle.join()
  4. handle.join 等待第一个线程完成。
  5. handles.fold 尝试访问迭代器的第二个元素。
  6. 第二个线程启动。
  7. 等等。

在折叠结果之前,您应该将句柄收集到向量中:

let handles: Vec<_> = (0..THREAD_COUNT)
.map(|thread_id| {
thread::spawn(move ||
// calculate the sum of all numbers from assigned to this thread
(thread_id * BATCH_SIZE + 1 .. (thread_id + 1) * BATCH_SIZE + 1)
.fold(0_u64,|sum, number| sum + number))
})
.collect();

或者您可以使用像 Rayon 这样的 crate 它提供并行迭代器。

关于multithreading - 为什么使用 Iterator::map 生成线程不能并行运行线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55490906/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com