gpt4 book ai didi

c++ - 防止线程不必要的退出并使池保持事件状态

转载 作者:行者123 更新时间:2023-12-03 12:45:45 25 4
gpt4 key购买 nike

假设我用 OMP_NUM_THREADS=16 调用一个程序。

第一个函数调用#pragma omp parallel for num_threads(16)

第二个函数调用#pragma omp parallel for num_threads(2)

第三个函数调用#pragma omp parallel for num_threads(16)

使用 gdb 调试显示在第二次调用时 14 线程退出。在第三次调用时,生成了 14 个新线程。

是否可以防止 14 线程在第二次调用时退出?谢谢。

证明 list 如下。

$ cat a.cpp
#include <omp.h>

void func(int thr) {
int count = 0;

#pragma omp parallel for num_threads(thr)
for(int i = 0; i < 10000000; ++i) {
count += i;
}
}

int main() {
func(16);

func(2);

func(16);

return 0;
}
$ g++ -o a a.cpp -fopenmp -g
$ ldd a
...
libgomp.so.1 => ... gcc-9.3.0/lib64/libgomp.so.1
...
$ OMP_NUM_THREADS=16 gdb a

...

Breakpoint 1, main () at a.cpp:13
13 func(16);
(gdb) n
[New Thread 0xffffbe24f160 (LWP 27216)]
[New Thread 0xffffbda3f160 (LWP 27217)]
[New Thread 0xffffbd22f160 (LWP 27218)]
[New Thread 0xffffbca1f160 (LWP 27219)]
[New Thread 0xffffbc20f160 (LWP 27220)]
[New Thread 0xffffbb9ff160 (LWP 27221)]
[New Thread 0xffffbb1ef160 (LWP 27222)]
[New Thread 0xffffba9df160 (LWP 27223)]
[New Thread 0xffffba1cf160 (LWP 27224)]
[New Thread 0xffffb99bf160 (LWP 27225)]
[New Thread 0xffffb91af160 (LWP 27226)]
[New Thread 0xffffb899f160 (LWP 27227)]
[New Thread 0xffffb818f160 (LWP 27228)]
[New Thread 0xffffb797f160 (LWP 27229)]
[New Thread 0xffffb716f160 (LWP 27230)]
15 func(2);
(gdb)
[Thread 0xffffba9df160 (LWP 27223) exited]
[Thread 0xffffb716f160 (LWP 27230) exited]
[Thread 0xffffbca1f160 (LWP 27219) exited]
[Thread 0xffffb797f160 (LWP 27229) exited]
[Thread 0xffffb818f160 (LWP 27228) exited]
[Thread 0xffffbd22f160 (LWP 27218) exited]
[Thread 0xffffb899f160 (LWP 27227) exited]
[Thread 0xffffbda3f160 (LWP 27217) exited]
[Thread 0xffffbb1ef160 (LWP 27222) exited]
[Thread 0xffffb91af160 (LWP 27226) exited]
[Thread 0xffffba1cf160 (LWP 27224) exited]
[Thread 0xffffb99bf160 (LWP 27225) exited]
[Thread 0xffffbb9ff160 (LWP 27221) exited]
[Thread 0xffffbc20f160 (LWP 27220) exited]
17 func(16);
(gdb)
[New Thread 0xffffbb9ff160 (LWP 27231)]
[New Thread 0xffffbc20f160 (LWP 27232)]
[New Thread 0xffffb99bf160 (LWP 27233)]
[New Thread 0xffffba1cf160 (LWP 27234)]
[New Thread 0xffffbda3f160 (LWP 27235)]
[New Thread 0xffffbd22f160 (LWP 27236)]
[New Thread 0xffffbca1f160 (LWP 27237)]
[New Thread 0xffffbb1ef160 (LWP 27238)]
[New Thread 0xffffba9df160 (LWP 27239)]
[New Thread 0xffffb91af160 (LWP 27240)]
[New Thread 0xffffb899f160 (LWP 27241)]
[New Thread 0xffffb818f160 (LWP 27242)]
[New Thread 0xffffb797f160 (LWP 27243)]
[New Thread 0xffffb716f160 (LWP 27244)]
19 return 0;

最佳答案

简单的答案是,GCC 不可能强制运行时保持线程。从粗略阅读 source code of libgomp , 没有可移植的或特定于供应商的 ICV 可以防止连续区域中过多空闲线程的终止。 (如果我错了,有人纠正我)

如果您确实需要依赖 OpenMP 运行时跨区域使用持久线程且团队规模各不相同的不可移植要求,那么请使用 Clang 或 Intel C++ 而不是 GCC。 Clang 的(实际上是 LLVM 的)OpenMP 运行时基于 Intel 的开源版本,它们都按照您想要的方式运行。同样,这不是可移植的,并且行为可能会在未来的版本中发生变化。相反,建议不要以其性能取决于 OpenMP 实现的特殊性的方式编写代码。例如,如果循环花费的时间比创建线程组多几个数量级(在现代系统中大约为几十微秒),那么运行时是否使用持久线程并不重要。

如果 OpenMP 开销确实是一个问题,例如,如果在循环中完成的工作不足以分摊开销,一个可移植的解决方案是提升并行区域,然后重新实现 for worksharing construct like in @dreamcrash's answer 或(ab)通过设置 block 大小来使用 OpenMP 的循环调度,这只会导致所需数量的线程处理该问题:

#include <omp.h>

void func(int thr) {
static int count;
const int N = 10000000;

int rem = N % thr;
int chunk_size = N / thr;

#pragma omp single
count = 0;

#pragma omp for schedule(static,chunk_size) reduction(+:count)
for(int i = 0; i < N-rem; ++i) {
count += i;
}

if (rem > 0) {
#pragma omp for schedule(static,1) reduction(+:count)
for(int i = N-rem; i < N; ++i) {
count += i;
}
}

#pragma omp barrier
}

int main() {
int nthreads = max of {16, 2, other values of thr};

#pragma omp parallel num_threads(nthreads)
{
func(16);

func(2);

func(16);
}

return 0;
}

您需要所有线程中大小完全相同的 block 。第二个循环用于处理 thr 不划分迭代次数的情况。此外,不能简单地对私有(private)变量求和,因此 count 必须共享,例如,通过使其成为 static。这很丑陋,并且拖延了一堆同步必需品,这些同步必需品的开销可能与生成新线程相当,并使整个练习毫无意义。

关于c++ - 防止线程不必要的退出并使池保持事件状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65324897/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com