我应该如何为并行计算生成线程?

时间:2015-06-29 00:08:02

标签: multithreading concurrency rust

今天,我进入了多线程。由于这是一个新概念,我认为我可以通过将简单的迭代转换为并行化的来开始学习。但是,我认为在我开始之前就陷入了困境。

最初,我的循环看起来像这样:

let stuff: Vec<u8> = items.into_iter().map(|item| {
    some_item_worker(&item)
}).collect();

我把相当多的东西放到了items中,完成计算花了大约0.05秒。所以,一旦我成功实现了多线程,我很高兴看到时间减少了!

当我使用线程时,我遇到了麻烦,可能是由于我的错误推理。

use std::thread;

let threads: Vec<_> = items.into_iter().map(|item| {
    thread::spawn(move || {
        some_item_worker(&item)
    })
}).collect(); // yeah, this is followed by another iter() that unwraps the values

我有一个四核CPU,这意味着我最多只能同时运行4个线程。我猜它是这样工作的:一旦迭代器启动,线程就会产生。每当线程结束时,另一个线程开始,这样在任何给定时间,4个线程同时运行。

结果是(经过一些重新运行)〜0.2秒完成相同的计算。显然,这里没有并行计算。我不知道为什么时间增加了4倍,但我确信我误会了什么。

由于这不是正确的方法,我应该如何修改程序以便线程并发执行?

编辑:

对不起,我错了~0.2秒。当我注意到通常的迭代运行2秒时,我醒来并再次尝试。事实证明,某些过程一直在大量消耗内存。当我重新启动系统并再次尝试线程迭代时,它运行了大约0.07秒。以下是每次运行的时间安排。

实际迭代(第一个):

0.0553760528564 seconds
0.0539519786835 seconds
0.0564560890198 seconds

螺纹一个:

0.0734670162201 seconds
0.0727820396423 seconds
0.0719120502472 seconds

我同意线程确实同时运行,但它似乎消耗了另外20毫秒来完成这项工作。我的实际目标是利用我的处理器并行运行线程并尽快完成工作。这会复杂吗?我该怎么做才能使这些线程并行运行,而不是并发?

2 个答案:

答案 0 :(得分:2)

  

我有一个四核CPU,这意味着我最多只能同时运行4个线程。

只有4个可以同时运行,但你当然可以创建超过4个......

  

每当一个线程结束时,另一个线程开始,这样在任何给定的时间,4个线程同时运行(这只是猜测)。

每当你有一个猜测时,你应该创建一个实验来判断你的猜测是否正确。这是一个:

use std::{iter, thread, time::Duration};

fn main() {
    let items: Vec<_> = iter::repeat(0).take(500).collect();

    let threads: Vec<_> = items
        .into_iter()
        .map(|_| {
            thread::spawn(move || {
                println!("Started!");
                thread::sleep(Duration::from_millis(500));
                println!("Finished!");
            })
        })
        .collect();

    for handle in threads {
        handle.join().unwrap()
    }
}

如果你运行它,你会看到&#34;开始!&#34;打印出500次,然后是500&#34;完成!&#34;

  

显然,这里没有并行计算

不幸的是,您的问题并没有充实,我们无法告诉您为什么您的时间过去了。在我提供的示例中,它需要不到600毫秒,所以它显然没有串行发生!

答案 1 :(得分:0)

创建一个线程需要付出代价。如果线程内的计算成本足够小,那么线程的成本或线程导致的低效率就会相形见绌。

例如,产生1000万个线程以使1000万u8s翻倍可能不值得。对它进行矢量化可能会产生更好的性能。

也就是说,通过并行化廉价任务,您仍然可以获得一些改进。但是你希望通过一个线程池使用更少的线程w /少量的线程(所以你在任何给定点创建(小)线程数,减少CPU争用)或更复杂的东西(在引擎盖下,api很简单)就像Rayon

// Notice `.par_iter()` turns it into a `parallel iterator`
let stuff: Vec<u8> = items.par_iter().map(|item| {
    some_item_worker(&item)
}).collect();