c++ - 防止线程不必要的退出并使池保持活动状态
问题描述
假设我用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
线程在第二次调用时退出?谢谢你。
证明清单如下。
$ 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 不可能强制运行时保留线程。通过粗略阅读 的源代码libgomp
,没有可移植或特定于供应商的 ICV 可以防止终止连续区域中多余的空闲线程。(如果我错了,有人纠正我)
如果您确实需要依赖OpenMP 运行时跨区域使用持久线程且团队规模不同的不可移植要求,那么请使用 Clang 或 Intel C++ 而不是 GCC 。Clang 的(实际上是 LLVM 的)OpenMP 运行时基于英特尔的开源版本,它们的行为都符合您的要求。同样,这不是可移植的,并且行为可能会在将来的版本中发生变化。相反,建议不要以这样一种方式编写代码,即其性能取决于 OpenMP 实现的特殊性。例如,如果循环比创建线程组花费的时间多几个数量级(在现代系统上大约为几十微秒),那么运行时是否使用持久线程并不重要。
如果 OpenMP 开销确实是一个问题,例如,如果在循环中完成的工作不足以分摊开销,则可移植的解决方案是提升并行区域,然后重新实现for
工作共享结构,如@dreamcrash 的答案或( ab) 通过设置块大小来使用 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;
}
您需要所有线程中大小完全相同的块。第二个循环用于处理thr
不划分迭代次数的情况。此外,不能简单地对私有变量求和,因此count
必须共享,例如,通过 make it static
。这很丑陋,并且拖累了一堆同步必需品,这些必需品的开销可能与产生新线程的开销相当,并使整个练习毫无意义。
推荐阅读
- android - 图像的可点击区域(鼠标悬停事件) - Jetpack Compose Desktop
- amazon-web-services - 使用无服务器框架部署 AWS Lambda 时缺少依赖项
- vue.js - Google 跟踪代码管理器分析选项无法正常工作
- html - 为什么页面加载时我的引导选项卡不显示?
- javascript - Javascript通过按键旋转播放器
- java - 在 Cloud Foundry 上使用 Tabula-py 时未找到 Java
- ruby-on-rails - 回形针上传/更新:Rails 6 上无法修改冻结字符串:“”错误
- javascript - 为什么这个 Jest 测试没有失败?
- sql - 根据另一个字段自动生成数字字段
- php - 当字符串小于 PHP 8 中的起始字符时 substr 返回一个空字符串