首页 > 解决方案 > 使用 readdir 在 for 循环中并行递归

问题描述

我想并行化一个 C 程序,该程序使用 OpenMP 和 C 递归计算目录及其子目录的大小。

我的问题是,当我使用 进入目录并使用opendir遍历子目录时,readdir我只能一个接一个地访问它们,直到到达最后一个子目录。这一切都按顺序运作良好。

然而,当并行化程序时,我认为将子目录的数量分成两半(甚至更小的分​​区)并使用 OpenMp 任务通过子目录递归是有意义的。

显然,由于 for 循环的结构,我不能简单地将问题大小(= 子目录的数量)分成两半,并且这样的循环不能使用#pragma omp for.

有人知道如何将此功能拆分为任务吗?任何帮助将不胜感激。

这是我的一些代码(我已经删除了我认为与这个问题无关的部分。)

int calculate_folder_size(const char *path) {

    struct stat sb;

    if (S_ISREG(sb.st_mode)) { // if it's a file, not a directory (base case)
        return sb.st_size;
    }

    DIR *folder = opendir(path);

    struct dirent *element;
    size_t size = 4096;

    for (element = readdir(folder); element != NULL; element = readdir(folder)) {

   //(...)
            if (element->d_type == DT_DIR) {
                // recursive call of calculate_folder_size
                size += calculate_folder_size(name);
            } else {
             //(...)
            }
        }
    }
    closedir(folder);
    return size;
}

标签: cparallel-processingtaskopenmp

解决方案


您需要一个支持 OpenMP 任务的现代编译器,它将 Visual C++ 从等式中移除。如果您使用的是这样的编译器,您需要做的就是将递归调用转换calculate_folder_size()为 OpenMP 任务:

int calculate_folder_size(const char *path) {
    struct stat sb;

    if (S_ISREG(sb.st_mode)) { // if it's a file, not a directory (base case)
        return sb.st_size;
    }

    DIR *folder = opendir(path);

    struct dirent *element;
    size_t size = 4096;

    for (element = readdir(folder); element != NULL; element = readdir(folder)) {
        //(...)
        if (element->d_type == DT_DIR) {
            // Make sure the task receives a copy of the path
            char *priv_name = strdup(name); // (1)
            // recursive call of calculate_folder_size
            //               (2)
            #pragma omp task shared(size) firstprivate(priv_name)
            {
                // (3)
                #pragma omp atomic update
                size += calculate_folder_size(priv_name);
                free(priv_name); // (4)
            }
        } else {
            //(...)
        }
    }
    // (5)
    #pragma omp taskwait

    closedir(folder);
    return size;
}

这里的重要部分是:

  1. 您需要向任务传递一个名称参数,该名称参数将一直存在并保留其值,直到任务被执行,这可能是将来的任何时间。因此,您需要制作 的副本name,例如,使用strdup(3).

  2. 任务应该记住 的当前值,priv_name因为它将在循环的下一次迭代中发生变化。因此,firstprivate治疗priv_name。它还需要能够size在父上下文中进行修改,因此shared需要它。

  3. 由于所有任务都在更新size父作用域中的同一个变量,因此需要使用atomic update.

  4. 不再需要私有名称,必须对其进行处置。

  5. 父任务应先等待所有子任务完成后再返回size

必须从并行区域内调用此函数才能并行完成其工作:

int size;

#pragma omp parallel
#pragma omp single
size = calculate_folder_size("/some/path");

限制事物仍然并行运行的并行深度可能是一个好主意。我把它留给你弄清楚如何:)


推荐阅读