1

假设我有一个包含 9000 多次迭代的 for 循环,我想用线程改进它,比如 10 次。

Function Something(){

    for ( i = 0; i < 9000 ){
        DoStuff();
    }
}

用我的 10 个线程覆盖 9000 次迭代的最佳方法是什么?我目前正在使用 C++99 和 win32 pthreads,但我认为这是一个通用问题。

提前致谢。

编辑:对于这个例子,假设 DoStuff() 处理繁重的处理,独立于其他迭代。此外,存在共享资源,但这些资源被互斥变量覆盖。

4

4 回答 4

3

答案真的取决于DoStuff()实际做了什么。如果您将某个大向量与另一个大(或小)向量相乘,那么将其分成 10 个部分可能并不难。这适用于每个计算独立于其他计算的任何 CPU 密集型工作。计算所有元素的总和也可以,但是你必须总结一个部分,然后存储结果,当所有线程完成时,总结不同的部分。

还有一些计算对并行化完全没用。使用 F(n) = F(n-1) + F(n-2) 方法计算斐波那契数在线程中根本不起作用,因为您需要上一步的结果才能计算当前步。

另一方面,如果DoStuff从单个文件中读取 1000 万条记录,那么拥有更多线程就不太可能有帮助 - 因为按顺序读取文件比分散读取要快一点,而且磁盘要大得多比处理器慢,所以你不会得到任何东西。

于 2013-06-11T17:02:48.550 回答
0

很大程度上取决于里面的东西DoStuff()。如果其中的数据依赖于其他迭代,或者访问已更新且必须跨DoStuff()运行共享的外部数据,那么线程甚至可能会减慢速度。如果DoStuff()能够独立运行并且有自己的位置来存储不与其他线程冲突的内存,并且需要足够长的时间来运行以克服设置线程并在完成时加入它们的初始开销,那么创建循环上方的 10 个线程,通过在每个线程中放置 900 次迭代来运行代码,并在完成时加入/杀死它们。或者使用线程池构造并让它为您完成。

通用问题的通用答案。

于 2013-06-11T16:58:27.527 回答
0

根据您的编辑,在我看来,可能有一个根本不涉及显式线程的解决方案。您可能完全可以使用 OpenMP 并行执行代码,而无需显式执行线程。这可能很简单,例如:

Function Something(){

    #pragma omp parallel for // ...
    for ( i = 0; i < 9000 ){
        DoStuff();
    }
}

在这里,...您可能需要(或想要)在此处添加更多注释。例如,您可以指定哪些变量将被共享,哪些变量将独立于每个线程等。

这可能没有编写自己的线程代码那么迷人,但它可能非常有效。特别是,OpenMP 运行时通常具有内置代码,用于根据可用的处理器资源确定要使用多少线程,因此不会明确使用 10 个线程——所以几年后,当你有一个具有 16 个内核的机器,您无需重写即可利用它们。

同时,OpenMP 确实有局限性。对于您描述的情况(并行执行循环迭代),它工作得很好。它几乎不适合其他一些场景(例如,创建一个执行管道,以便一个处理步骤发生在一个内核上,下一步在下一个内核上进行,等等)。

于 2013-06-11T18:41:44.097 回答
0

一种方法是将循环的部分委托给不同的线程。让一个线程处理 0-999 范围,第二个线程处理 1000-1999 范围,依此类推。伪代码如下:

Function Thread(int start, int count){

    for ( i = start; i < (start + count); ++i ){
        DoStuff();
    }
}

Function Something(){

    for ( i = 0; i < 9; ++i ){
        SpawnThread(Thread, (i * 1000), 1000);
    }

}
于 2013-06-11T17:25:37.667 回答