使用 OpenMP 任务的带有 break 的并行 for 循环

Parallel for loop with break using OpenMP tasks

我是 OpenMP 的初学者,正在学习有关任务的知识,并希望将它们应用到以下场景中。这个例子本身是完全没用的;重点是并行化包含中断条件并执行 IO 操作的循环。出于这个问题的目的,我保留了通用的中断条件。

我尝试了以下方法,但程序变得非常慢(可能是因为我创建了太多任务...)。我也尝试使用 taskloop 构造,但结果几乎相同。现在,在考虑中断条件之前,我正在尝试弄清楚如何使该死的东西 运行 相当快。关于我应该如何进行的任何建议?

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

const int max_iters = 500000;
const char cmd_format[300] = "echo Iteration %d\n";

int main(int argc, char **argv) {
  FILE *fp;
  char ret[200];
  char cmd[400];

  #pragma omp parallel
  #pragma omp single
  for (int i = 0; i < max_iters; i++) {
    #pragma omp task default(none) \
     firstprivate(i,cmd,fp,ret) \
     shared(cmd_format,max_iters)
    {
      sprintf((char *)&cmd, cmd_format, i);
      fp = popen(cmd, "r");
      while (!feof(fp)) {
        fgets((char *)&ret, 200, fp);
        if (/* some condition */) {
          printf("Done\n");
          i = max_iters;
        }
      }
      pclose(fp);
    }
  }

  return EXIT_SUCCESS;
}

我还没有测试下面的代码,但它应该仍然显示了这个想法。问题是您不能轻易中止并行计算。在 OpenMP 中执行此操作的方法是取消,请参阅 https://www.openmp.org/spec-html/5.1/openmpse28.html#x144-1560002.20

这是使用 cancel 结构的代码:

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

const int max_iters = 500000;
const char cmd_format[300] = "echo Iteration %d\n";

int main(int argc, char **argv) {
  FILE *fp;
  char ret[200];
  char cmd[400];

  #pragma omp parallel
  #pragma omp single
  #pragma omp taskgroup
  for (int i = 0; i < max_iters; i++) {
    #pragma omp task default(none) \
     firstprivate(i,cmd,fp,ret) \
     shared(cmd_format,max_iters)
    {
      sprintf((char *)&cmd, cmd_format, i);
      fp = popen(cmd, "r");
      while (!feof(fp)) {
        fgets((char *)&ret, 200, fp);
        if (/* some condition */) {
          #pragma omp atomic update
          i = max_iters;
          printf("Done\n");
          #pragma omp cancel taskgroup
        }
      }
      pclose(fp);
    }
  }

  return EXIT_SUCCESS;
}

在单曲之后我引入了一个 taskgroup 结构,这样现在创建的所有任务都有一个逻辑分组。一旦您的条件得到满足,cancel 构造将触发取消 taskgroup.

的任务

需要注意的一件事是:这不是立即采取的行动。如果一个任务已经开始执行,它不会被终止,除非它到达一个取消点或者它到达它的代码区域的末尾。已生成但位于任务池中且尚未开始执行的任务将被丢弃。

因此,在使用此功能时,您需要让您的代码意识到这一点并处理这些情况。因此,例如,如果多个任务的终止条件可以为真,则需要使代码足够灵活,以便两个或多个任务可以输入 if 语句并尝试记录结果(在根据您的情况,将 i 设置为 max_iters)。这就是为什么我添加了 atomic 构造以确保在更新 i 变量时不会发生竞争,但延迟的任务可能仍会覆盖较早任务的结果。由于您的真实代码中必须发生的事情取决于您的代码实际做了什么,我无法提供更好的建议。