【问题标题】:Memory leak when using OpenMP使用 OpenMP 时的内存泄漏
【发布时间】:2010-12-02 15:15:45
【问题描述】:

以下测试用例在使用 OpenMP 时在“post MT section”消息之后的循环中在 32 位机器上耗尽内存(抛出 std::bad_alloc),但是,如果 OpenMP 的#pragmas 被注释掉,代码运行正常,所以看起来当内存在并行线程中分配时,它没有正确释放,因此我们用完了内存。

问题是下面的内存分配和删除代码是否有问题,或者这是 gcc v4.2.2 或 OpenMP 中的错误?我也尝试了 gcc v4.3 并得到了同样的失败。

int main(int argc, char** argv)
{
    std::cout << "start " << std::endl;

    {
            std::vector<std::vector<int*> > nts(100);
            #pragma omp parallel
            {
                    #pragma omp for
                    for(int begin = 0; begin < int(nts.size()); ++begin) {
                            for(int i = 0; i < 1000000; ++i) {
                                    nts[begin].push_back(new int(5));
                            }
                    }
            }

    std::cout << "  pre delete " << std::endl;
            for(int begin = 0; begin < int(nts.size()); ++begin) {
                    for(int j = 0; j < nts[begin].size(); ++j) {
                            delete nts[begin][j];
                    }
            }
    }
    std::cout << "post MT section" << std::endl;
    {
            std::vector<std::vector<int*> > nts(100);
            int begin, i;
            try {
              for(begin = 0; begin < int(nts.size()); ++begin) {
                    for(i = 0; i < 2000000; ++i) {
                            nts[begin].push_back(new int(5));
                    }
              }
            } catch (std::bad_alloc &e) {
                    std::cout << e.what() << std::endl;
                    std::cout << "begin: " << begin << " i: " << i << std::endl;
                    throw;
            }
            std::cout << "pre delete 1" << std::endl;

            for(int begin = 0; begin < int(nts.size()); ++begin) {
                    for(int j = 0; j < nts[begin].size(); ++j) {
                            delete nts[begin][j];
                    }
            }
    }

    std::cout << "end of prog" << std::endl;

    char c;
    std::cin >> c;

    return 0;
}

【问题讨论】:

  • 当我在使用英特尔编译器构建的 Windows 下运行它时,由于达到 32 位进程的 2gb 限制,我的分配在第一个循环中开始失败。 OpenMP 的开销是否可能只是将您的进程推到了平台上的任何限制之上?
  • @Scott Danahy 尝试更改测试用例以将所有分配减半,此测试使用 4 GB 限制。

标签: multithreading gcc memory-leaks openmp allocator


【解决方案1】:

将第一个 OpenMP 循环从 1000000 更改为 2000000 将导致相同的错误。这表明内存不足问题与 OpenMP 堆栈限制有关。

尝试在 bash 中将 OpenMP 堆栈限制设置为无限制

ulimit -s unlimited

您还可以更改 OpenMP 环境变量 OMP_STACKSIZE 并将其设置为 100MB 或更大。

更新 1:我将第一个循环更改为

{
    std::vector<std::vector<int*> > nts(100);
    #pragma omp for schedule(static) ordered
    for(int begin = 0; begin < int(nts.size()); ++begin) {
        for(int i = 0; i < 2000000; ++i) {
            nts[begin].push_back(new int(5));
        }
    }

    std::cout << "  pre delete " << std::endl;
    for(int begin = 0; begin < int(nts.size()); ++begin) {
        for(int j = 0; j < nts[begin].size(); ++j) {
            delete nts[begin][j]
        }
    }
}

然后,我在主线程的 i=1574803 处遇到内存错误。

更新 2:如果您使用的是 Intel 编译器,您可以在代码顶部添加以下内容,它会解决问题(前提是您有足够的内存来承受额外的开销)。

std::cout << "Previous stack size " << kmp_get_stacksize_s() << std::endl;
kmp_set_stacksize_s(1000000000);
std::cout << "Now stack size " << kmp_get_stacksize_s() << std::endl;

更新 3:为了完整起见,就像另一位成员提到的那样,如果您正在执行一些数值计算,最好将所有内容预分配在单个 new float[1000000] 中,而不是使用 OpenMP 来执行1000000 个分配。这也适用于分配对象。

【讨论】:

  • 当你把第一个循环改成2000000时,分配新内存失败的进程总分配大小是多少?
  • i=1574803 当它崩溃时。请参阅我的更新 1。
  • @Dat Chu 所以不使用英特尔编译器(即使用 gcc)OMP_STACKSIZE env var 设置是可行的方法,因为 kmp_set_stacksize_s() 在 gcc 中不可用?
  • 是的。确保以 KB 为单位指定值或提供正确的后缀 gcc.gnu.org/onlinedocs/libgomp/OMP_005fSTACKSIZE.html
  • @DatChu 我尝试使用 OMP_STACKSIZE=100M 运行,也尝试了 ulimit,但在问题的更新示例代码中使用 begin=76 和 i=1048576 对我来说仍然失败,但如果未使用 OpenMP,代码在没有耗尽内存的情况下完成。
【解决方案2】:

我在其他地方发现了这个问题,但没有使用 OpenMP 而只是使用 pthreads。多线程时的额外内存消耗似乎是标准内存分配器的典型行为。通过切换到Hoard 分配器,额外的内存消耗就消失了。

【讨论】:

    【解决方案3】:

    您为什么使用int* 作为内部向量成员?这非常浪费——对于每个 vector 条目,您有 4 个字节(严格来说是sizeof(int))的数据和 2-3 倍的堆控制结构。使用vector&lt;int&gt; 试试这个,看看它是否运行得更好。

    我不是 OpenMP 专家,但这种用法的不对称性似乎很奇怪 - 您在并行部分填充向量并在非并行代码中清除它们。无法告诉你这是否是错误的,但它“感觉”是错误的。

    【讨论】:

    • @Steve Townsend 此测试用例旨在显示问题,而不是实际使用的代码。每个条目中包含五个整数的数组有助于消耗内存以演示泄漏。提高代码的内存效率只会导致失败延迟,直到消耗更多内存。
    • @WilliamKF - 我明白了。我认为您需要一位 OpenMP 大师来发表评论。该问题询问有关内存分配和删除的任何问题,对我来说,RAII 似乎更可取。我正在为您的 q 添加 +1,因为我自己对答案很感兴趣。
    • 由于代码是为了演示问题,感觉有点尴尬,但在 OpenMP 结构之外进行清理和后处理并不少见。
    • @Steve Townsend 我认为这里的关键是关闭 OpenMP 会导致测试用例不会耗尽内存。
    • 是的,这就是我想知道在非并行代码中清理是否有问题。另一个问题可能是没有任何线程私有变量的循环嵌套。
    猜你喜欢
    • 1970-01-01
    • 2014-12-11
    • 2023-03-24
    • 1970-01-01
    • 2016-02-07
    • 2021-07-06
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多