首先,为了确定,既然您声明htop
显示正在使用单个内核,请确保您在编译器中启用了 OpenMP 支持。这样做的选项-fopenmp
适用于 GCC、-xopenmp
Sun/Oracle 编译器和-openmp
Intel 编译器。
其次,n = 20
对于并行实现来说,可能的界限太低了。一个无耻的插件 - 请参阅我的一位同事几个月前举办的 OpenMP 研讨会上的课程材料。从幻灯片 20 开始,这里讨论了几个带有任务的并行版本。
第三,ptime
是一个 Solaris 命令,并非特定于 SPARC,因为它在 x86 版本中也可用。许多与进程相关的 Solaris 命令的p
名称中都有前缀。请注意,在您的情况下time
,更有可能是 Bash 提供的内置实现,而不是独立的二进制文件。
第四,这可能是您问题的真正答案-您parallel
的代码中缺少一个区域,因此任务指令根本不起作用:)您应该将代码重写如下:
long comp_fib_numbers(int n)
{
long fnm1, fnm2, fn;
if ( n == 0 || n == 1 ) return(n);
// In case the sequence gets too short, execute the serial version
if ( n < 20 )
{
return(comp_fib_numbers(n-1)+comp_fib_numbers(n-2));
}
else
{
#pragma omp parallel // <--- You are missing this one parallel region
{
#pragma omp single
{
#pragma omp task shared(fnm1)
fnm1 = comp_fib_numbers(n-1);
#pragma omp task shared(fnm2)
fnm2 = comp_fib_numbers(n-2);
}
#pragma omp taskwait
}
fn = fnm1 + fnm2;
return(fn);
}
}
if
您可以通过使用子句切换并行区域使代码更加简洁:
long comp_fib_numbers(int n)
{
long fnm1, fnm2, fn;
if ( n == 0 || n == 1 ) return(n);
#pragma omp parallel if(n >= 20)
{
#pragma omp single
{
#pragma omp task shared(fnm1)
fnm1 = comp_fib_numbers(n-1);
#pragma omp task shared(fnm2)
fnm2 = comp_fib_numbers(n-2);
}
#pragma omp taskwait
}
fn = fnm1 + fnm2;
return(fn);
}
如果n
恰好小于 20,则并行区域将执行单线程。由于并行区域通常在单独的函数中提取,因此仍然会有额外的函数调用,除非编译器选择生成重复代码。这就是为什么建议将串行实现提取到它自己的函数中:
long comp_fib_numbers_serial(int n)
{
if ( n == 0 || n == 1 ) return(n);
return (comp_fib_numbers_serial(n-1) + comp_fib_numbers_serial(n-2));
}
long comp_fib_numbers(int n)
{
long fnm1, fnm2, fn;
if ( n < 20 ) return comp_fib_numbers_serial(n);
#pragma omp parallel
{
#pragma omp single
{
#pragma omp task shared(fnm1)
fnm1 = comp_fib_numbers(n-1);
#pragma omp task shared(fnm2)
fnm2 = comp_fib_numbers(n-2);
}
#pragma omp taskwait
}
fn = fnm1 + fnm2;
return(fn);
}
编辑:现在我已经查看了您链接到的代码,我可以看到调用comp_fib_numbers
被嵌入到一个parallel
区域中。parallel
因此,如果您的代码中已经有一个缺失区域,请忽略我对缺失区域的评论。为了完整起见,我将把它留在这里。尝试调整并行版本和串行版本之间发生切换的值。在现代处理器上,它可能会很高,而且您看到的示例已经很老了。OMP_DYNAMIC
还要通过将环境变量设置为false
(或FALSE
)或调用omp_set_dynamic(0);
并行区域之前的某个位置来确保不使用动态团队。
您还没有说明您的编译器是什么,但请注意,自 4.4 版起,GCC 支持 OpenMP 3.0,自 11.0 版起由英特尔编译器支持,自 I_dont_know 版起由 Sun/Oracle 编译器支持,并且 Visual C/C++ 编译器根本不支持 OpenMP 3.0 .
在四插槽 Intel Xeon X7350 系统(带 FSB 的旧前 Nehalem 系统)上观察到的加速
$ time OMP_NUM_THREADS=1 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=1 ./fib.x 40 1.86s user 0.00s system 99% cpu 1.866 total
$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40 1.96s user 0.00s system 169% cpu 1.161 total
截止设置为25
(似乎是 X7350 的最佳值):
$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40 1.95s user 0.00s system 169% cpu 1.153 total
将截止设置为25
和串行实现的单独函数:
$ time OMP_NUM_THREADS=2 ./fib.x 40
finonacci(40) = 102334155
OMP_NUM_THREADS=2 ./fib.x 40 1.52s user 0.00s system 171% cpu 0.889 total
看看用户时间如何减少了大约 400 毫秒。这是因为消除了开销。
这些是使用您链接到的站点中的代码测量的。使用的编译器是 64 位 Scientific Linux 6.2 系统上的 GCC 4.4.6。