递归并行功能不使用所有内核

时间:2013-11-22 10:07:53

标签: c++ performance openmp

我最近实现了一个递归negamax算法,我使用OpenMP进行了并行化。

有趣的是:

#pragma omp parallel for
for (int i = 0; i < (int) pos.size(); i++)
{
    int val = -negamax(pos[i].first, -player, depth - 1).first;

    #pragma omp critical
    if (val >= best)
    {
        best = val;
        move = pos[i].second;
    }
}

在我的英特尔酷睿i7(4个物理内核和超线程)上,我发现了一些非常奇怪的事情:在运行算法时,它没有使用所有8个可用线程(逻辑核心),而只使用4个。

任何人都可以解释为什么会如此?我理解算法不能很好地扩展的原因,但为什么不使用所有可用的核心?

编辑:我将主题更改为核心,因为它更能表达我的问题。

3 个答案:

答案 0 :(得分:2)

除了尽可能多的线程外,最佳并行度级别还有一些额外的考虑因素。例如,操作系统用于将单个进程的所有线程调度到单个处理器以优化缓存性能(除非程序员明确地更改它)。

我想OpenMP在执行这样的代码时会做出类似的考虑,你不能总是假设执行了最大的线程数/

答案 1 :(得分:2)

首先,检查您是否有足够的迭代次数pos.size()。显然这应该是一个足够的数字。


递归并行是一种有趣的模式,但它可能不适用于OpenMP,除非您使用的是OpenMP 3.0的task,Cilk或TBB。有几件事需要考虑:

(1)为了使用递归并行性,您通常需要显式调用omp_set_nested(1)。 AFAIK,OpenMP的大多数实现都不会递归地生成parallel for,因为它可能最终会创建数千个物理线程,只会爆炸您的操作系统。

在OpenMP 3.0的task之前,OpenMP具有逻辑并行任务到物理任务的一对一映射。因此,它在这种递归并行性方面不会很好。尝试一下,但即使创建了数千个线程也不要感到惊讶!

(2)如果你真的想在传统的OpenMP中使用递归并行,你需要实现控制活动线程数的代码:

if (get_total_thread_num() > TOO_MANY_THREADS) {
  // Do not use OpenMP
  ...
} else {
#pragma omp parallel for
  ...
}

(3)您可以考虑OpenMP 3.0的task。在您的代码中,由于递归,可能会有大量并发任务。为了有效地在并行机器上工作,必须有一个有效的映射算法,这些逻辑并发任务到物理线程(或逻辑处理器,核心)。 OpenMP中的原始递归并行性将创建实际的物理线程。 OpenMP 3.0的task没有。

您可以参考我以前与递归并行相关的答案:C OpenMP parallel quickSort

(4)英特尔的Cilk Plus和TBB支持完全嵌套和递归并行。在我的小测试程序中,性能远远优于OpenMP 3.0。但是,那是3年前的事了。您应该检查最新的OpenMP实现。


我对negamaxminimax没有详细了解。但是,我的直觉说使用递归模式和锁定不太可能加速。一个简单的谷歌搜索给了我:http://supertech.csail.mit.edu/papers/dimacs94.pdf

  

“但是,negamax不是一种有效的串行搜索算法,因此,它   将它并行化是没有意义的。“

答案 2 :(得分:0)

Whaddya意味着所有8个可用线程?这样的CPU可能会运行100个线程!您可能认为具有超线程的4个内核相当于8个线程,但您的OpenMP安装可能不会。

检查:

  • 是否已创建并设置了环境变量OMP_NUM_THREADS?如果它设置为4,那么您的答案就是,您的OpenMP环境配置为最多只启动4个线程。
  • 如果尚未设置该环境变量,请调查OpenMP例程omp_get_num_threads()omp_set_num_threads()的使用和影响。如果已设置环境变量,则omp_set_num_threads()将在运行时覆盖它。
  • 8个超线程是否优于4个真实线程。
  • 是否超额订阅,例如OMP_NUM_THREADS设置为16,除了破坏性能之外还会做任何其他事情。