C++:tbb

问题描述

Valgrind 报告了我正在使用的应用程序的内存泄漏,在 tbb 中:

==00:00:00:42.164 12570== 126 bytes in 7 blocks are definitely lost in loss record 5,026 of 6,496
==00:00:00:42.164 12570==    at 0x4C29BC3: malloc (vg_replace_malloc.c:299)
==00:00:00:42.164 12570==    by 0x9387AF9: strdup (in /usr/lib64/libc-2.17.so)
==00:00:00:42.164 12570==    by 0x1E47A8A2: ??? (in /usr/lib64/libtbb.so.2)
==00:00:00:42.164 12570==    by 0x1E4833AC: ??? (in /usr/lib64/libtbb.so.2)
==00:00:00:42.164 12570==    by 0x1E481653: ??? (in /usr/lib64/libtbb.so.2)
==00:00:00:42.164 12570==    by 0x1E481878: ??? (in /usr/lib64/libtbb.so.2)
==00:00:00:42.164 12570==    by 0x5F7CEA4: start_thread (in /usr/lib64/libpthread-2.17.so)
==00:00:00:42.164 12570==    by 0x93F98DC: clone (in /usr/lib64/libc-2.17.so)

gdb 能够找到更详细的 strdup 回溯:

(gdb)
#0  0x00007ffff35d4ae0 in strdup () from /lib64/libc.so.6
#1  0x00007fffe05bf8a3 in __itt_thread_set_name_init_3_0(char const*) () from /lib64/libtbb.so.2
#2  0x00007fffe05c83ad in tbb::internal::market::create_one_job() () from /lib64/libtbb.so.2
#3  0x00007fffe05c6654 in tbb::internal::rml::private_worker::run() () from /lib64/libtbb.so.2
#4  0x00007fffe05c6879 in tbb::internal::rml::private_worker::thread_routine(void*) () from /lib64/libtbb.so.2
#5  0x00007ffff6a87ea5 in start_thread () from /lib64/libpthread.so.0
#6  0x00007ffff36468dd in clone () from /lib64/libc.so.6
(gdb) p $rdi
$1 = 140736957592936
(gdb) p (char *)(140736957592936)
$2 = 0x7fffe05d3568 "TBB Worker Thread"

鉴于此,泄漏的内存实际上是一个字符串,用于给线程命名,等于“TBB Worker Thread”,看这里: https://github.com/oneapi-src/oneTBB/blob/master/src/tbb/market.cpp#L599

在此处调用的 strdup: https://github.com/oneapi-src/oneTBB/blob/master/src/tbb/tools_api/ittnotify_config.h#L476

似乎永远不会被释放。我浏览了 tbb 源,但没有看到任何地方可以免费使用。

我已经从该应用程序中提取了 tbb 逻辑到一个独立的程序中,该程序根据内存增加的明显线性速率重现了泄漏(尽管由于某种原因没有出现在 valgrind 上):

#include <tbb/parallel_for.h>
#include <thread>
#include <cstdio>

// compile with g++ tbb.cpp -std=c++11 /lib64/libtbb.so.2 -lpthread -o tbb

struct ApplyFoo {

    void operator()( const tbb::blocked_range<int>& r ) const {
        return;
    }
};

void myfunc() {
    tbb::parallel_for(tbb::blocked_range<int>(0,12000,10000),ApplyFoo());
}

void functhread() {

    std::thread *threads[10];

    while (1) {
        for (int i=0;i<100000;i++) {
            for (int i=0;i<10;i++) {
                threads[i] = new std::thread(myfunc);
            }
    
            for (int i=0;i<10;i++) {
                threads[i]->join();
                delete threads[i];
            }       
        }
        printf("Sleeping Now...\n");
        sleep(30);
    }
}

int main() {

    std::thread t1(functhread);
    std::thread t2(functhread);

    t1.join();
    t2.join();
}

似乎调用 tbb::parallel_for 的线程必须自己死亡才能发生泄漏。 tbb 可能有一些线程本地存储,存储在调用 parallel_for 的线程中,没有被清理。这就是为什么需要像上面看到的线程层次结构来重现这一点。但是,我在 tbb 中找不到任何说明需要任何额外清理的文档,或者这种范式对于 tbb 是非法的。

30 秒的睡眠是完全没有必要的,但它是一个“宽限时间”,让 tbb 有机会清理东西,以证明这不仅仅是程序重载本身的问题。在这个宽限期内,我们可以看到线程数量下降到 3 个(主线程 + 两个 std::threads),这意味着所有 tbb 线程都已退出,但随着时间的推移,内存使用仍然存在并且非常明显地增加

我在 stackoverflow 上搜索了 tbb 泄漏,但这些帖子都没有描述适用于此的情况。

root@localhost:~ [22:00:55] $ ps -o RSS -p $(pidof tbb)
  RSS
37352
root@localhost:~ [22:00:55] $ ps -o RSS -p $(pidof tbb)
  RSS
61552
root@localhost:~ [22:04:31] $ ps -o RSS -p $(pidof tbb)
  RSS
61960
root@localhost:~ [22:04:32] $ ps -o RSS -p $(pidof tbb)
  RSS
62156
root@localhost:~ [22:04:33] $ ps -o RSS -p $(pidof tbb)
  RSS
62392
root@localhost:~ [22:14:36] $ ps -o RSS -p $(pidof tbb)
  RSS
131340
root@localhost:~ [22:15:00] $ ps -o RSS -p $(pidof tbb)
  RSS
133508
root@localhost:~ [22:19:47] $ ps -o RSS -p $(pidof tbb)
  RSS
167192
root@localhost:~ [22:19:48] $ ps -o RSS -p $(pidof tbb)
  RSS
207644

编辑:在这里澄清问题:这是 tbb 中的错误,还是应用程序滥用了库(例如,缺少一些清理步骤等)?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...