C++ C++;OpenMP斐波那契:1个线程的执行速度比4个线程快得多

C++ C++;OpenMP斐波那契:1个线程的执行速度比4个线程快得多,c++,openmp,C++,Openmp,我试图理解为什么OpenMP上1个线程的运行速度要比4个线程的运行速度快得多。下面的代码实际上基于一个类似的问题:但是当尝试实现其中一个建议的答案时,我没有得到预期的加速,这表明我做错了什么(不确定是什么)。当在4个线程上运行下面的程序时,是否比在1个线程上运行更快?在4核上运行时,我的速度降低了10倍(我应该得到适度的加速,而不是明显的降低) intfib(intn) { 如果(n==0 | | n==1) 返回n; if(n

我试图理解为什么OpenMP上1个线程的运行速度要比4个线程的运行速度快得多。下面的代码实际上基于一个类似的问题:但是当尝试实现其中一个建议的答案时,我没有得到预期的加速,这表明我做错了什么(不确定是什么)。当在4个线程上运行下面的程序时,是否比在1个线程上运行更快?在4核上运行时,我的速度降低了10倍(我应该得到适度的加速,而不是明显的降低)

intfib(intn)
{
如果(n==0 | | n==1)
返回n;
if(n<20)//编辑代码以包括截止
返回fib(n-1)+fib(n-2);
国际关系,a,b;
#pragma omp任务共享(a)
a=fib(n-1);
#pragma omp任务共享(b)
b=fib(n-2);
#pragma omp taskwait
res=a+b;
返回res;
}
int main(){
omp_集合_嵌套(1);
omp_设置_数量_线程(4);
双启动时间=omp\U get\U wtime();
#pragma-omp并行
{
#布拉格omp单曲
{

你试过很多次了吗

在多线程中,初始化CPU内核上的工作需要一些时间。对于在单个内核上完成速度非常快的较小作业,线程会因此降低作业的速度

如果作业通常花费的时间超过秒,而不是毫秒,则多线程显示速度提高

线程还存在另一个瓶颈。如果您的代码试图创建太多的线程(主要是通过递归方法),这可能会导致所有正在运行的线程延迟,从而导致大量的延迟

在这个wiki页面中,我们提到了这个函数,并建议手动关闭它。这个函数需要有两个版本,当线程太深时,它会继续使用单线程进行递归

编辑:在进入OMP区域之前,需要增加截止变量


以下代码用于OP测试的测试目的

#define CUTOFF 5
int fib_s(int n)
{
    if (n == 0 || n == 1)
        return n;
    int res, a, b;
    a = fib_s(n - 1);
    b = fib_s(n - 2);
    res = a + b;
    return res;
}
int fib_m(int n,int co)
{
    if (co >= CUTOFF) return fib_s(n);
    if (n == 0 || n == 1)
        return n;
    int res, a, b;
    co++;
#pragma omp task shared(a)
    a = fib_m(n - 1,co);
#pragma omp task shared(b)
    b = fib_m(n - 2,co);
#pragma omp taskwait
    res = a + b;
    return res;
}

int main()
{
    omp_set_nested(1);
    omp_set_num_threads(4);
    double start_time = omp_get_wtime();
#pragma omp parallel
    {
#pragma omp single
        {
            cout << fib_m(25,1) << endl;
        }
    }
    double time = omp_get_wtime() - start_time;
    std::cout << "Time(ms): " << time * 1000 << std::endl;
    return 0;
}

我相信我不知道如何告诉编译器在一定深度后不要创建并行任务,因为:omp_set_max_active_levels似乎没有效果,omp_set_nested被弃用(尽管它也没有效果)

因此,我必须手动指定在哪个级别之后不创建更多任务。哪个IMHO令人伤心。我仍然相信应该有办法做到这一点(如果有人知道,请告诉我)。下面是我如何尝试的,输入大小为20的并行版本比串行版本运行快一点(比如70-80%的时间)。 参考:代码取自课程作业(未提供解决方案,因此我不知道如何有效地完成):

#包括
#包括
#包括
内部纤维(内部n,内部rec_高度)
{
int x=1,y=1;
if(n<2)
返回n;
int t计数=0;
if(rec_height>0)//令人惊讶的是,如果没有这个检查,并行代码比串行代码慢(我相信不需要它,我只是不知道如何使用OpneMP)
{
rec_高度-=1;
#pragma omp任务共享(x)
x=fib(n-1,记录高度);
#pragma omp任务共享(y)
y=fib(n-2,记录高度);
#pragma omp taskwait
}
否则{
x=fib(n-1,记录高度);
y=fib(n-2,记录高度);
}
返回x+y;
}
int main()
{
int tot_螺纹=16;
int recDepth=(int)log2f(tot_线程);
如果((int)pow(2,recDepth))
即使是大数字(>35),fibonacci在一个线程上比在多个线程上快得多。@user308485,甚至35都不是你想象的那么大的数字。我已经测试了这个嵌套方法,时间似乎与40后有关。在这个域中再试一次。使用更多的内核仍然没有任何好处。你刚刚测试过这段代码吗?你能在som中使用更多的内核获得一些加速吗e特定大小?看看
fib()
子例程。它执行一次加法(例如计算)并“生成”两个线程(例如开销),因此,在这里使用
OpenMP
毫无益处也就不足为奇了(一个
for
循环甚至比递归实现更快)@user308485,我有一个编辑来解决你的问题,但是保留了前面的部分以备将来可能引用,你需要为你的函数添加一个截止点。别忘了,你需要两个版本的函数。哦,顺便说一下,你的代码不做并行循环,而是生成一个线程树,这就是为什么它需要太多时间的原因。fi中的每个数字bonacci序列取决于前面的数字。为什么这会有助于多线程处理?您不能期望任何性能提升。可以做的一件事是缓存已计算的fibonacci值。这会给您带来显著的加速。为什么
omp\u set\u嵌套(1)
?我没有看到任何嵌套的并行部分。请提供1)具体的性能结果2)有关系统的信息3)a 4)编译器、版本和命令我在2x12 core Haswell E5/GCC上做了一些实验,加速比强烈依赖于截止参数。对于截止20,我的加速比大约为5,对于截止42,加速比大约为10对于24个线程。我计算了
fib(48)
,较低的输入太快,无法得到相关的结果。
#define CUTOFF 5
int fib_s(int n)
{
    if (n == 0 || n == 1)
        return n;
    int res, a, b;
    a = fib_s(n - 1);
    b = fib_s(n - 2);
    res = a + b;
    return res;
}
int fib_m(int n,int co)
{
    if (co >= CUTOFF) return fib_s(n);
    if (n == 0 || n == 1)
        return n;
    int res, a, b;
    co++;
#pragma omp task shared(a)
    a = fib_m(n - 1,co);
#pragma omp task shared(b)
    b = fib_m(n - 2,co);
#pragma omp taskwait
    res = a + b;
    return res;
}

int main()
{
    omp_set_nested(1);
    omp_set_num_threads(4);
    double start_time = omp_get_wtime();
#pragma omp parallel
    {
#pragma omp single
        {
            cout << fib_m(25,1) << endl;
        }
    }
    double time = omp_get_wtime() - start_time;
    std::cout << "Time(ms): " << time * 1000 << std::endl;
    return 0;
}
co=1   14.5s
co=2    9.5s
co=3    6.4s
co=10   7.5s
co=15   7.0s 
co=20   8.5s
co=21 >18.0s
co=22 >40.0s
#include <stdio.h>
#include <omp.h>
#include <math.h>

int fib(int n, int rec_height)
{
  int x = 1, y = 1;
  if (n < 2) 
      return n;
  int tCount = 0;

  if (rec_height > 0)   //Surprisingly without this check parallel code is slower than serial one (I believe it is not needed, I just don't know how to use OpneMP)
  {
   rec_height -= 1;
  #pragma omp task shared(x)
  x = fib(n - 1, rec_height);
  #pragma omp task shared(y)
  y = fib(n - 2, rec_height);
  #pragma omp taskwait
  }
  else{
    x = fib(n - 1, rec_height);
    y = fib(n - 2, rec_height);
  }
  return x+y;

}


int main()
{
  int tot_thread = 16;
  int recDepth = (int)log2f(tot_thread);
  if( ((int)pow(2, recDepth)) < tot_thread) recDepth += 1;
  printf("\nrecDepth: %d\n",recDepth);
  omp_set_max_active_levels(recDepth);
  omp_set_nested(recDepth-1);

  int n,fibonacci;
  double starttime;
  printf("\nPlease insert n, to calculate fib(n): %d\n",n);
  scanf("%d",&n);
  omp_set_num_threads(tot_thread);
  starttime=omp_get_wtime();
  #pragma omp parallel
  {
   #pragma omp single
   {
    fibonacci=fib(n, recDepth);
   }
  }
  printf("\n\nfib(%d)=%d \n",n,fibonacci);
  printf("calculation took %lf sec\n",omp_get_wtime()-starttime);
  return 0;
}