Rai*_*inn 5 c++ multithreading openmp
我试图在程序的不同部分使用不同数量的线程,以实现最大的加速。但是,发现使用num_threads子句切换线程号会产生大量开销。我正在寻找对此的解释,因为根据我的理解,线程池应始终包含给定数量的线程,而不管调用的实际数量是多少。我也在寻找与此相对的可能解决方法。谢谢。
样例代码:
#include<cstdio>
#include<omp.h>
void omp_sum(int ntd) {
int s = 0;
#pragma omp parallel num_threads(ntd)
{
int i = omp_get_thread_num();
#pragma omp atomic
s += i;
}
}
int main()
{
int N = 100;
int NT1 = 6, NT2 = 12;
double t;
t = omp_get_wtime();
for(int n=0;n<N;n++) {
omp_sum(NT1);
}
printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
t = omp_get_wtime();
for(int n=0;n<N;n++) {
omp_sum(NT2);
}
printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
t = omp_get_wtime();
for(int n=0;n<N;n++) {
omp_sum(NT1);
omp_sum(NT1);
}
printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
t = omp_get_wtime();
for(int n=0;n<N;n++) {
omp_sum(NT2);
omp_sum(NT2);
}
printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
t = omp_get_wtime();
for(int n=0;n<N;n++) {
omp_sum(NT1);
omp_sum(NT2);
}
printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
}
Run Code Online (Sandbox Code Playgroud)
样本输出(以美国为单位):
1034.069001
1058.620000
1034.572000
2210.681000
18234.355000
Run Code Online (Sandbox Code Playgroud)
编辑:运行代码的工作站具有2个六核Intel E5-2630L CPU,因此应该总共有12个硬件核心和24个超线程。我在GCC 4.8.2中使用Fedora 19。
我可以在我的四核系统/八超线程系统上使用 GCC 4.8 (g++ -O3 -fopenmp foo.cpp) 重现您的结果。我把N1改为4,N2改为8。
你的功能omp_sum很简单
pushq %rbx
movq %rdi, %rbx
call omp_get_thread_num
movq (%rbx), %rdx
lock addl %eax, (%rdx)
popq %rbx
ret
Run Code Online (Sandbox Code Playgroud)
这是循环的汇编代码
for(int n=0;n<N;n++) {
omp_sum(NT1);
omp_sum(NT2);
}
.L10
leaq 32(%rsp), %rsi
xorl %ecx, %ecx
movl $4, %edx
movl $_Z7omp_sumi._omp_fn.0, %edi
movl $0, 28(%rsp)
movq %rbx, 32(%rsp)
call GOMP_parallel
leaq 32(%rsp), %rsi
xorl %ecx, %ecx
movl $8, %edx
movl $_Z7omp_sumi._omp_fn.0, %edi
movl $0, 28(%rsp)
movq %rbx, 32(%rsp)
call GOMP_parallel
subl $1, %ebp
jne .L10
Run Code Online (Sandbox Code Playgroud)
这与循环的组装几乎相同
for(int n=0;n<N;n++) {
omp_sum(NT2);
omp_sum(NT2);
}
Run Code Online (Sandbox Code Playgroud)
唯一的变化是movl $4, %edx代替movl $8, %edx. 因此很难看出是什么原因导致了问题。所有的魔法都发生在 GOMP_parallel 中。人们必须查看 GOMP_parallel 的源代码,但我的猜测是 GOMP_parallel 检查并行调用中最后使用的线程数,如果新的并行调用使用不同数量的线程,则需要切换一些开销。这个开销比你的简单函数大得多。
但我不确定为什么这会成为一个问题。在实践中,使用如此短的并行部分是没有意义的(一个可以并行化一个循环,N 会更大),所以开销应该不成问题。
编辑: OpenMP 3.1 规范的第 2.41 节标题为“确定并行区域的线程数”给出了确定线程数的算法。GCC-4.8 中 GOMP_parallel 的源代码显示它调用的第一个函数是gomp_resolve_num_threads.