我考虑过哪些因素会影响OpenMP中的静态调度开销。 在我看来,它受到以下因素的影响:
但是我错过了其他因素吗?也许任务的大小,......?
此外:开销线性地依赖于迭代次数? 在这种情况下,我希望有静态调度和4个内核,开销随着4 * i次迭代线性增加。到目前为止正确吗?
编辑: 我只对静态(!)调度开销感兴趣。我不是在谈论线程启动开销和在同步和关键部分开销上花费的时间。
答案 0 :(得分:4)
您需要将OpenMP的开销分开,以创建一个团队/线程池,以及每个线程在for循环中操作单独的迭代器集合的开销。
静态调度很容易手工实现(有时非常有用)。让我们考虑一下我认为最重要的两个静态调度schedule(static)
和schedule(static,1)
,然后我们可以将其与schedule(dynamic,chunk)
进行比较。
#pragma omp parallel for schedule(static)
for(int i=0; i<N; i++) foo(i);
相当于(但不一定等于)
#pragma omp parallel
{
int start = omp_get_thread_num()*N/omp_get_num_threads();
int finish = (omp_get_thread_num()+1)*N/omp_get_num_threads();
for(int i=start; i<finish; i++) foo(i);
}
和
#pragma omp parallel for schedule(static,1)
for(int i=0; i<N; i++) foo(i);
相当于
#pragma omp parallel
{
int ithread = omp_get_thread_num();
int nthreads = omp_get_num_threads();
for(int i=ithread; i<N; i+=nthreads) foo(i);
}
由此可以看出,实现静态调度非常简单,因此开销可以忽略不计。
另一方面,如果你想手动实施schedule(dynamic)
(与schedule(dynamic,1)
相同),那就更复杂了:
int cnt = 0;
#pragma omp parallel
for(int i=0;;) {
#pragma omp atomic capture
i = cnt++;
if(i>=N) break;
foo(i);
}
这需要OpenMP&gt; = 3.1。如果你想用OpenMP 2.0(对于MSVC)这样做,你需要使用像这样的关键
int cnt = 0;
#pragma omp parallel
for(int i=0;;) {
#pragma omp critical
i = cnt++;
if(i>=N) break;
foo(i);
}
这是schedule(dynamic,chunk)
的等价物(我没有使用原子访问优化它):
int cnt = 0;
int chunk = 5;
#pragma omp parallel
{
int start, finish;
do {
#pragma omp critical
{
start = cnt;
finish = cnt+chunk < N ? cnt+chunk : N;
cnt += chunk;
}
for(int i=start; i<finish; i++) foo(i);
} while(finish<N);
}
显然使用原子访问会导致更多开销。这也说明了为schedule(dynamic,chunk)
使用较大的块可以减少开销的原因。