c++ - OpenMP 行为 - 嵌套多线程
问题描述
我的问题与嵌套并行和 OpenMP 有关。让我们从以下单线程代码片段开始:
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
现在假设我们要performAnotherTask
使用 OpenMP 并行调用。
所以我们得到以下代码:
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
#pragma omp parallel for
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
我的理解是调用performAnotherTask
将并行执行,默认情况下,openMP 将尝试使用您机器上的所有可用线程(也许这个假设是不正确的)。
假设我们现在还想并行化调用,performTask
以便我们得到以下代码:
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
#pragma omp parallel for
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
#pragma omp parallel for
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
这将如何运作?两个 for 循环仍然是多线程的吗?我们能说一下每个循环将使用的线程数吗?有没有办法强制内部 for 循环(within performTask
)只使用单个线程,而外部 for 循环使用所有可用线程?
解决方案
在您的最后一个示例中,执行行为取决于一些环境设置。
首先,OpenMP 确实支持这种模式,但默认情况下禁用嵌套并行区域中的并行执行。要启用它,您必须在代码中设置OMP_NESTED=true
或调用omp_set_nested(1)
。然后启用对嵌套并行执行的支持。
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
#pragma omp parallel for
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
omp_set_nested(1);
#pragma omp parallel for
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
其次,当 OpenMP 到达外部parallel
区域时,它可能会抓取所有可用内核并假设它可以在它们上执行线程,因此您可能希望减少外部级别的线程数,以便某些内核可用于嵌套区域。比如说,如果你有 32 个核心,你可以这样做:
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
#pragma omp parallel for num_threads(8)
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
omp_set_nested(1);
#pragma omp parallel for num_threads(4)
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
外部并行区域将使用 4 个线程执行,每个线程将使用 8 个线程执行内部区域。请注意,4 个外部线程中的每一个都将是四个同时执行的嵌套并行区域的主线程之一。如果您想更加灵活,您可以使用环境变量注入每个级别使用的线程数OMP_NUM_THREADS
。如果您将其设置为,OMP_NUM_THREADS=4,8
您将获得与上面我发布的第一个代码片段相同的行为。
编码模式的问题是您需要小心平衡每个级别,以免系统过载或嵌套并行区域之间的负载不平衡。另一种解决方案是改用 OpenMP 任务:
void performAnotherTask() {
// DO something here
}
void performTask() {
// Do other stuff here
#pragma omp taskloop
for (size_t i = 0; i < 100; ++i) {
performAnotherTask();
}
}
int main() {
omp_set_nested(1);
#pragma omp parallel
#pragma omp single
#pragma omp taskloop
for (size_t i = 0; i < 100; ++i) {
performTask();
}
return 0;
}
在这里,每个taskloop
构造都将生成 OpenMP 任务,这些任务计划在由parallel
代码中的单个区域创建的线程上执行。需要注意的是,任务的行为本质上是动态的,因此您可能会丢失局部性属性,因为您不知道任务将在系统中的确切位置执行。
推荐阅读
- html - 简单的主页 HTML 查询
- java - 如何在 Android Studio 的评论和日志中搜索文本
- unity3d - 尝试在 Unity3D 中创建 RayCast 时出现异常
- angular - 角。用组件替换字符串中的单词
- python - 具有 NULL 和空白值的字段
- r - 登录后捕获会话
- gitlab - 我如何将来自 gitlab 的传入 webhook 设置为信使(不是松弛,不是著名的)?
- python - 如何在 SQL 查询中执行此操作,根据此条件查找相似行百分比匹配?
- android - 为什么 RecyclerView 没有从 Viewmodel 中的 LiveData 获取更新?
- php - 小部件页面在开发人员模式下未加载和接收错误