我想编写一个c ++代码,用于并行计算Fibonacci数,并使用OpenMP工具。我知道,使用#pragma omp task
,代码将是:
int 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;
}
}
以这种方式计算并不酷,因为每个线程都在等待其子线程完成其工作,因此整个过程仍然是顺序的!
我想用这个等式编写并行代码:
F(n + k)= F(n + 1)* F(k)+ F(n)* F(k - 1)
有人帮我这样做吗?
这种方式是“酷”并且并行工作。请注意,taskwait
确实允许线程执行其他任务,例如由它自己的子任务创建的任务。只是为了说明可能的执行计划:
* creating a task , # actually waiting for all child tasks
------- time --->
Thread 1 {fib(4): **b {fib(2): ** {fib(1): ret 1} # ret 1+0} # ret 2+1
Thread 2 {fib(3): ** {fib(1): ret 1} # ret 1+1}
Thread 3 {fib(2): ** {fib(0): ret 0} ret 1+0}
Thread 4 {fib(1): ret 1}{fib(0): ret 0}
当然,与迭代计算相比,这种代码仍然非常低效。此外,在使用递归算法任务的实际代码中,应在某个递归深度之后停止创建新任务以管理任务创建开销。
下面是一个替代的并行解决方案,它使用黄金比率直接计算fib(n)
和fib(n+1)
,然后为每个线程顺序计算序列的其余部分。我得到了rosettacode的直接解决方案。
问题是n>72
的直接计算溢出所以这个方法几乎肯定比顺序计算前71个数字要慢,并且在任何情况下,n>92
的64位整数都会溢出。
#include <math.h>
#include <stdio.h>
#include <inttypes.h>
#include <omp.h>
uint64_t fib(unsigned m) { // Direct Calculation, correct for abs(m) <= 71
double sqrt5r = 1.0 / sqrt(5.0);
double golden = (1.0 + sqrt(5.0))/2.0;
return rint(pow(golden, m)*sqrt5r);
}
void fib_sequence(unsigned n, uint64_t *f) {
#pragma omp parallel
{
size_t t = omp_get_thread_num();
size_t nt = omp_get_num_threads();
int x0 = (t+0)*n/nt;
int x1 = (t+1)*n/nt;
f[x0 + 0] = fib(x0 + 0);
f[x0 + 1] = fib(x0 + 1);
for(int i=x0+2; i<x1; i++) {
f[i] = f[i-2] + f[i-1];
}
}
}
int main(void) {
int n = 71;
uint64_t f1[n], f2[n];
for(int i=0; i<n; i++) f1[i] = fib(i);
fib_sequence(n, f2);
for(int i=0; i<n; i++) if(f1[i] != f2[i]) printf("%d\n", i);
for(int i=0; i<n; i++) printf("F%d: %" PRId64 " %" PRId64 "\n", i, f1[i], f2[i]);
}
当我找到n个Fibonacci数的并行化时,我的一个朋友做了一个很酷的技巧。 我们都知道使用matrix exponentation可以在log(k)时间内找到kth fibonacci数。 斐波纳契(j)也只取决于斐波那契(j - 1)和斐波那契(j - 2) 假设你有四个线程,那么第一个线程将被赋予迭代地找到0到(n / 4)-1个斐波纳契数的任务。现在第二个线程必须找到从n / 4到2n / 4 - 1的斐波纳契数,但为此它需要fibo(n / 4 - 1)。那么现在的诀窍就是这个线程会在O(log(n))时间内使用矩阵指数来计算fibo(n / 4)和fibo(n / 4 + 1)(没有多少时间正确且聪明,因为我们不等待第一个线程的最终结果)。现在,一旦第二个线程获得fibo(n / 4)和fibo(n / 4 + 1),它将与其他线程并行发现从n / 4到2n / 4 - 1的斐波纳契数。 类似地,第三个线程将首先使用矩阵指数查找fibo(2n / 4)和fibo(2n / 4 + 1),它将与其他线程并行查找从2n / 4到3n / 4-1的斐波纳契数。 最后的第四个线程也会做同样的事情。 现在,如果你要求性能,可用的p theads中的每个线程将花费最多2log(n)的时间并进行n / p迭代,这将花费O(n / p)时间。所以每个线程最多只能完成O(n / p + log(n))的工作,加速将是pn / n + plog(n) 希望有所帮助!