考虑以下代码,取自here。对于此代码,我得到以下执行时间:
time ./fibomp 40
Number of threads (OpenMP v200805): 2
finonacci(40) = 102334155
real 0m3.193s
user 0m3.180s
sys 0m0.000s
$ export OMP_NUM_THREADS=1
$ time ./fibomp 40
Number of threads (OpenMP v200805): 1
finonacci(40) = 102334155
real 0m3.224s
user 0m3.216s
sys 0m0.000s
所以你可以看到,速度并不快,绝对不是他在2011年11月1日上午1:41发送的电子邮件中提到的2倍加速Ruud。我在双核机器上运行它(可能就是这样吗?)。我究竟做错了什么? (BTW,BONUS指出,什么是ptime
命令?某些SPARC Unix命令?)
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 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);
}
}
答案 0 :(得分:4)
首先,为了确保,因为您声明htop
显示正在使用单个核心,请确保您已在编译器中启用了OpenMP支持。执行此操作的选项是GCC为-fopenmp
,Sun / Oracle编译器为-xopenmp
,英特尔编译器为-openmp
。
其次,n = 20
对于并行实现来说可能太低了。一个无耻的插件 - 请参阅我的一位同事几个月前在OpenMP上的研讨会上的this course material。从幻灯片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);
来确保不使用动态团队。 / p>
您还没有说明您的编译器是什么,但请注意,自版本4.4以来,GCC支持OpenMP 3.0,自版本11.0以来由英特尔编译器支持OpenMP 3.0,自版本I_dont_know以来由Sun / Oracle编译器支持,并且Visual C不支持/ C ++编译器。
观察到四插槽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。