C ++ OpenMP Fibonacci:1个线程的执行速度比4个线程快得多

时间:2018-05-15 00:55:29

标签: c++ openmp

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

int fib(int n)
  {
    if(n == 0 || n == 1)
        return n;
    if (n < 20) //EDITED CODE TO INCLUDE CUTOFF
        return fib(n-1)+fib(n-2); 
    int res, a, b;
    #pragma omp task shared(a)
    a = fib(n-1);
    #pragma omp task shared(b)
    b = fib(n-2);
    #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(25) << endl;
    }
  }
  double time = omp_get_wtime() - start_time;
  std::cout << "Time(ms): " << time*1000 << std::endl;
  return 0;
}

2 个答案:

答案 0 :(得分:2)

您是否尝试了大量的数据?

在多线程中,初始化CPU核心的工作需要一些时间。对于在单个核心上执行速度非常快的小型作业,线程会因此而减慢作业。

如果作业通常需要的时间超过秒,而不是毫秒,则多线程会显示速度增加。

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

在这个OpenMP/Tasks维基页面中,提到了它并建议手动切断。需要有2个版本的函数,当线程太深时,它会继续使用单线程进行递归。

编辑:在进入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;
}

结果: 当CUTOFF值设置为10时,计算第45个项的时间不到8秒。

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

答案 1 :(得分:0)

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

因此,我必须手动指定在哪个级别之后不创建更多任务。哪个恕我直言是可悲的。我仍然相信应该有一种方法(如果有人知道,请告诉我)。这是我尝试的方法,在输入大小为20的并行版本运行之后,运行速度比串行运行的快一些(例如70-80%的时间)。 参考:从课程分配中获取的代码(未提供解决方案,所以我不知道如何高效地执行):https://www.cs.iastate.edu/courses/2018/fall/com-s-527x

#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;
}