首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenMP任务--以及"OpenMP if“的成本

OpenMP任务--以及"OpenMP if“的成本
EN

Stack Overflow用户
提问于 2020-09-25 10:07:00
回答 1查看 416关注 0票数 2

我正在努力理解OpenMP的task的工作方式。

因此,我从可能最简单的测试开始,遵循OpenMP 4.5的斐波纳契计算示例:

代码语言:javascript
复制
// Listing 1

#include <omp.h>
#include <stdio.h>

long fib(int n)
{
    int i, j;
    if (n<2)
        return n;
    else {
        #pragma omp task shared(i)
        i=fib(n-1);
        #pragma omp task shared(j)
        j=fib(n-2);
        #pragma omp taskwait
        return i+j;
    }
}

int main()
{
    #pragma omp parallel
    #pragma omp single
    {
        long res = fib(27);
        printf("fib(27)=%ld\n", res);
    }
}

很明显,我们将在这里启动大量的任务--因此,OpenMP版本比正常版本慢得令人吃惊,这一点也不足为奇:

代码语言:javascript
复制
$ gcc -O2  fib_slow.c
$ time ./a.out
fib(27)=196418

real    0m0.003s
user    0m0.000s
sys     0m0.000s

$ gcc -O2  fib_slow.c -fopenmp
$ time ./a.out
fib(27)=196418

real    0m0.243s
user    0m0.468s
sys     0m0.080s

这个测试是在一个双核VM中运行的。注意,time报告的用户时间是实时时间的两倍,这意味着我们确实使用了第二个核心;但是我们基本上把所有时间浪费在无用的任务处理工作上,而不是实际的计算上。

很公平--这个例子的文本实际上警告我们,这仅仅是一个为教育目的而做的例子。

由于我们正在双核机器上进行测试,也许使用OpenMP的"if“结构在最顶层只启动两个线程更简单:一个计算fib(N-2)和一个fib(N-1)。

代码语言:javascript
复制
// Listing 2

#include <omp.h>
#include <stdio.h>

long fib(int val)
{
    if (val < 2)
        return val;

    long total = 0;
    {
        #pragma omp task shared(total) if(val==45)
        total += fib(val-1);
        #pragma omp task shared(total) if(val==45)
        total += fib(val-2);
        #pragma omp taskwait
    }
    return total;
}

int main()
{
    #pragma omp parallel
    #pragma omp single
    {
        long res = fib(45);
        printf("fib(45)=%ld\n", res);
    }
}

假设我对"if“的理解是正确的,这应该只在顶层启动两个任务(当输入为45),因此将更好地利用我们的两个核心。

我还把测试输入增加到了45,这样才能持续更长的时间。

代码语言:javascript
复制
$ gcc -O2  fib_nice.c
$ time ./a.out
fib(45)=1134903170

real    0m8.196s
user    0m8.192s
sys     0m0.000s

$ gcc -O2  fib_nice.c -fopenmp
$ time ./a.out
fib(45)=1134903170

real    1m33.237s
user    2m33.348s
sys     0m0.012s

哦-哦-这绝对不是我预料的那样。

为什么?

也许我使用的是OpenMP“如果”构造错误(尽管GCC没有告诉我我这么做了)--但可以肯定的是,我会自己决定产生一项任务:

代码语言:javascript
复制
// Listing 3

#include <omp.h>
#include <stdio.h>

long fib(int val)
{
    if (val < 2)
        return val;

    long total = 0;
    {
        if (val == 45) {
            #pragma omp task shared(total)
            total += fib(val-1);
            #pragma omp task shared(total)
            total += fib(val-2);
            #pragma omp taskwait
        } else
            return fib(val-1) + fib(val-2);
    }
    return total;
}

int main()
{
    #pragma omp parallel
    #pragma omp single
    {
        long res = fib(45);
        printf("fib(45)=%ld\n", res);
    }
}

别管在total上比赛的潜力--这不是重点,我只想看到我的第二个核心做些什么来改善时间。

做了吗?

代码语言:javascript
复制
$ gcc -O2  fib_nicer.c
$ time ./a.out
fib(45)=1134903170

real    0m7.974s
user    0m7.968s
sys     0m0.000s

$ gcc -O2  fib_nicer.c -fopenmp
$ time ./a.out
fib(45)=1134903170

real    0m8.773s
user    0m14.300s
sys     0m0.000s

显然,自己决定自己生成一个任务大大提高了OpenMP的执行时间。不知道为什么。

但我们还是比单核执行慢.即使第一核心做fib(43)和第二核心做fib(44)应该有所帮助。

难道是OpenMP #pragma在运行时花费了我们的时间--以至于他们使整个努力失效了吗?

让我们做一个最后的实验-以最愚蠢的方式:

代码语言:javascript
复制
// Listing 4

#include <omp.h>
#include <stdio.h>

long fib_naive(int val)
{
    if (val < 2)
        return val;
    else
        return fib_naive(val-1) + fib_naive(val-2);
}

long fib(int val)
{
    long total = 0;
    {
        #pragma omp task shared(total)
        total += fib_naive(val-1);
        #pragma omp task shared(total)
        total += fib_naive(val-2);
        #pragma omp taskwait
    }
    return total;
}

int main()
{
    #pragma omp parallel
    #pragma omp single
    {
        long res = fib(45);
        printf("fib(45)=%ld\n", res);
    }
}

这基本上是手动生成两个线程。当然,这一定有效..。

代码语言:javascript
复制
$ gcc -O2  fib.c
$ time ./a.out
fib(45)=1134903170

real    0m8.738s
user    0m8.728s
sys     0m0.004s

$ gcc -O2  fib.c -fopenmp
$ time ./a.out
fib(45)=1134903170

real    0m5.446s
user    0m8.928s
sys     0m0.004s

事实上,我们只花了5.4秒就完成了,而单线程执行的8.7秒。我的理论是,清单3中的if (产生顶级线程)最终花费很大,因为它是对计算中的每一个添加执行的;每个递归调用都必须经过它。

除此之外,如果你发现我所遵循的步骤有什么问题,请告诉我--因为我目前的看法是OpenMP的if是.比正常代码if慢得多。

提前感谢您的见解/建议。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-09-26 18:53:32

我打开了一个关于此的GCC的libgomp票 --正如您在那里所读到的,Jakub Jelinek解释说,OpenMP task中的"if(false)“并不等同于没有生成任务--事实上,与任务相关的数据结构被创建,父任务被挂起,并且这个新的子任务立即开始运行--父任务完成后立即恢复。不用说,这不仅仅是运行代码.

此外,Jakub还指出,在非OpenMP递归中,会发生ttail递归优化--即使使用了"mergeable“子句,OpenMP也不会发生这种情况。

可以说,我学到了很多-谢谢,雅库布。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64062046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档