C ++ 2011包含非常酷的新功能,但我找不到很多并行化for循环的示例。 所以我非常天真的问题是:如何将一个简单的for循环(如使用“omp parallel for”)与std :: thread并行化? (我搜索一个例子)。
非常感谢。
答案 0 :(得分:31)
std::thread
并不一定意味着平行循环。它意味着构建类似parallel_for算法的构造的低级抽象。如果你想要平行你的循环,你应该自己编写一个parallel_for算法,或者使用现有的提供基于任务的并行性的库。
以下示例显示了如何平行简单循环,但另一方面也显示了缺点,例如缺少负载平衡和简单循环的复杂性。
typedef std::vector<int> container;
typedef container::iterator iter;
container v(100, 1);
auto worker = [] (iter begin, iter end) {
for(auto it = begin; it != end; ++it) {
*it *= 2;
}
};
// serial
worker(std::begin(v), std::end(v));
std::cout << std::accumulate(std::begin(v), std::end(v), 0) << std::endl; // 200
// parallel
std::vector<std::thread> threads(8);
const int grainsize = v.size() / 8;
auto work_iter = std::begin(v);
for(auto it = std::begin(threads); it != std::end(threads) - 1; ++it) {
*it = std::thread(worker, work_iter, work_iter + grainsize);
work_iter += grainsize;
}
threads.back() = std::thread(worker, work_iter, std::end(v));
for(auto&& i : threads) {
i.join();
}
std::cout << std::accumulate(std::begin(v), std::end(v), 0) << std::endl; // 400
使用提供parallel_for
模板的库,可以将其简化为
parallel_for(std::begin(v), std::end(v), worker);
答案 1 :(得分:4)
显然,这取决于你的循环是什么,你如何选择paralellize,以及你如何管理线程的生命周期。
我正在阅读the book from the std C++11 threading library(这也是boost.thread维护者之一并撰写Just Thread),我可以看到&#34;它取决于&#34;。
现在为了让您了解使用新标准线程的基础知识,我建议您阅读本书,因为它提供了大量示例。 另外,请查看http://www.justsoftwaresolutions.co.uk/threading/和https://stackoverflow.com/questions/415994/boost-thread-tutorials
答案 2 :(得分:3)
由于我们仍然主要使用pthread,因此无法提供C ++ 11特定的答案。但是,作为一个与语言无关的答案,你通过将其设置为在一个单独的函数(线程函数)中运行来并行化。
换句话说,你有一个像:
这样的功能def processArraySegment (threadData):
arrayAddr = threadData->arrayAddr
startIdx = threadData->startIdx
endIdx = threadData->endIdx
for i = startIdx to endIdx:
doSomethingWith (arrayAddr[i])
exitThread()
并且,在主代码中,您可以用两个块处理数组:
int xyzzy[100]
threadData->arrayAddr = xyzzy
threadData->startIdx = 0
threadData->endIdx = 49
threadData->done = false
tid1 = startThread (processArraySegment, threadData)
// caveat coder: see below.
threadData->arrayAddr = xyzzy
threadData->startIdx = 50
threadData->endIdx = 99
threadData->done = false
tid2 = startThread (processArraySegment, threadData)
waitForThreadExit (tid1)
waitForThreadExit (tid2)
(请记住,在主线程开始为线程2修改它之前,应该确保线程1已将数据加载到其本地存储中,可能使用互斥锁或使用<结构的em> array ,每个线程一个。)
换句话说,仅仅修改for
循环以使其并行运行并不是一件简单的事情,尽管这样会很好,例如:
for {threads=10} ({i} = 0; {i} < ARR_SZ; {i}++)
array[{i}] = array[{i}] + 1;
相反,它需要重新安排代码以利用线程。
当然,您必须确保并行处理数据是有意义的。如果你将每个数组元素设置为前一个数组加1,那么没有多少并行处理会有所帮助,因为你必须先等待先修改前一个元素。
上面的这个特例只是使用传递给线程函数的参数来指定它应该处理的数组的哪个部分。线程函数本身包含完成工作的循环。
答案 3 :(得分:3)
使用this课程,您可以这样做:
Range based loop (read and write)
pforeach(auto &val, container) {
val = sin(val);
};
Index based for-loop
auto new_container = container;
pfor(size_t i, 0, container.size()) {
new_container[i] = sin(container[i]);
};
答案 4 :(得分:1)
AFAIK是一种并行化循环的最简单方法,如果你确定没有可能的并发访问,那就是使用OpenMP。
除了LLVM之外,所有主要编译器都支持它(截至2013年8月)。示例:
for(int i = 0; i < n; ++i)
{
tab[i] *= 2;
tab2[i] /= 2;
tab3[i] += tab[i] - tab2[i];
}
这很容易并行化:
#pragma omp parallel for
for(int i = 0; i < n; ++i)
{
tab[i] *= 2;
tab2[i] /= 2;
tab3[i] += tab[i] - tab2[i];
}
但是,请注意,只有大量值才能有效。
如果使用g ++,另一种非常C ++ 11的方法是使用lambda和for_each,并使用gnu并行扩展(可以在场景后面使用OpenMP):
__gnu_parallel::for_each(std::begin(tab), std::end(tab), [&] ()
{
stuff_of_your_loop();
});
但是,for_each主要用于数组,向量等...
但是,如果您只想通过使用Range
和begin
方法创建end
类来迭代一个范围,那么您可以“欺骗”它,这将大多增加一个int。
请注意,对于执行数学操作的简单循环,#include <numeric>
和#include <algorithm>
中的算法都可以使用G ++进行并行化。
答案 5 :(得分:1)
使用std :: thread和lambda表达式定义宏:
#ifndef PARALLEL_FOR
#define PARALLEL_FOR(INT_LOOP_BEGIN_INCLUSIVE, INT_LOOP_END_EXCLUSIVE,I,O) \ \
{ \
int LOOP_LIMIT=INT_LOOP_END_EXCLUSIVE-INT_LOOP_BEGIN_INCLUSIVE; \
std::thread threads[LOOP_LIMIT]; auto fParallelLoop=[&](int I){ O; }; \
for(int i=0; i<LOOP_LIMIT; i++) \
{ \
threads[i]=std::thread(fParallelLoop,i+INT_LOOP_BEGIN_INCLUSIVE); \
} \
for(int i=0; i<LOOP_LIMIT; i++) \
{ \
threads[i].join(); \
} \
} \
#endif
用法:
int aaa=0;
PARALLEL_FOR(0,90,i,
{
aaa+=i;
});
它的丑陋但它有效。