我有一个可以并行化的C ++程序。我正在使用Visual Studio 2010,32位编译。
简而言之,程序的结构如下
#define num_iterations 64 //some number
struct result
{
//some stuff
}
result best_result=initial_bad_result;
for(i=0; i<many_times; i++)
{
result *results[num_iterations];
for(j=0; j<num_iterations; j++)
{
some_computations(results+j);
}
// update best_result;
}
由于每个some_computations()
是独立的(读取了一些全局变量,但没有修改全局变量),我并行化了内部for
- 循环。
我的第一次尝试是 boost :: thread ,
thread_group group;
for(j=0; j<num_iterations; j++)
{
group.create_thread(boost::bind(&some_computation, this, result+j));
}
group.join_all();
结果很好,但我决定尝试更多。
我尝试了 OpenMP 库
#pragma omp parallel for
for(j=0; j<num_iterations; j++)
{
some_computations(results+j);
}
结果比boost::thread
的结果差。
然后我尝试了 ppl 库并使用了parallel_for()
:
Concurrency::parallel_for(0,num_iterations, [=](int j) {
some_computations(results+j);
})
结果最差。
我发现这种行为非常令人惊讶。由于OpenMP和ppl是为并行化而设计的,因此我期望得到比boost::thread
更好的结果。我错了吗?
为什么boost::thread
会给我更好的结果?
答案 0 :(得分:10)
OpenMP或PPL不会让人感到悲观。他们只是按照他们的说法去做,但是当你尝试对paralellize循环时,你应该考虑一些事情。
如果没有看到你如何实施这些事情,很难说真正的原因是什么。
此外,如果每次迭代中的操作都依赖于同一循环中的任何其他迭代,那么这将产生争用,这将减慢速度。您还没有显示some_operation
函数实际执行的操作,因此很难判断是否存在数据依赖关系。
可以真正并行化的循环必须能够使每次迭代完全独立于所有其他迭代运行,并且在任何迭代中都不会访问共享内存。因此,您最好将内容写入局部变量,然后在最后复制。
并非所有循环都可以并行化,它非常依赖于正在完成的工作类型。
例如,有助于并行化的是在屏幕缓冲区的每个像素上进行的工作。每个像素完全独立于所有其他像素,因此,一个线程可以进行一次循环迭代并完成工作,而不需要等待迭代之间循环内的共享内存或数据依赖。
此外,如果你有一个连续的数组,这个数组可能部分在一个缓存行中,如果你在线程A中编辑元素5然后在线程B中更改元素6,你可能会得到缓存争用,这也将放慢速度,因为它们会驻留在同一个缓存行中。称为虚假共享的现象。
进行循环并行化时需要考虑很多方面。
答案 1 :(得分:3)
简而言之,openMP
主要基于共享内存,需要额外的任务管理和内存管理成本。 ppl
旨在处理通用数据结构和算法的通用模式,它带来了额外的复杂性成本。它们都有额外的CPU成本,但是你的简单下降boost
线程不会(boost
线程只是简单的API包装)。这就是为什么它们都比boost
版本慢的原因。并且,由于示例计算彼此独立,没有同步,openMP
应该接近boost
版本。
它出现在简单的场景中,但是,对于复杂的场景,使用复杂的数据布局和算法,它应该依赖于上下文。