英特尔 tbb 内存开销
intel tbb memory overhead
我们在重用使用英特尔 TBB 函数的线程时遇到了高内存开销。我们期望一旦线程完成给定的工作负载,它就会释放相应的内存。然而,情况似乎并非如此,即使线程执行工作单元之间有很长的停顿。
我们准备了一个例子来说明问题:
int main() {
blocking_queue<size_t> command_input_queue;
tbb::atomic<size_t> count = 1;
//workers
std::vector<std::thread> worker;
for(size_t i = 0; i < 15; i++) {
worker.push_back(std::thread([&command_input_queue, &count](){
while(true)
{
size_t size;
//wait for work..
command_input_queue.wait_and_pop(size);
//do some work with Intel TBB
std::vector<int32_t> result(size);
for(size_t i = 0; i < result.size(); i++) {
result[i] = i % 1000;
}
tbb::parallel_sort(result.begin(), result.end());
size_t local_count = count++;
std::cout << local_count << " work items executed " << std::endl;
}
}));
}
//enqueue work
size_t work_items = 15;
for(size_t i = 0; i < work_items ; i++) {
command_input_queue.push(10 * 1000 * 1000);
}
while(true) {
boost::this_thread::sleep( boost::posix_time::seconds(1) );
if(count > 15) {
break;
}
}
//wait for more commands
std::cout << "Wait" << std::endl;
boost::this_thread::sleep( boost::posix_time::seconds(60) );
//----!During the wait, while no thread is active,
//the process still claims over 500 MB of memory!----
for(size_t i = 0; i < 15; i++) {
command_input_queue.push(1000 * 1000);
}
...
在示例中我们启动了 15 个工作线程。他们等待任务并执行 tbb::parallel_sort 并在完成后释放所有资源。
问题是在处理完所有任务后,所有工作人员都在等待新任务,该进程仍然占用 500MB 内存。
像 valgrind 的 massif 这样的工具没有告诉我们内存在哪里被声明。
我们将程序链接到 libtbb.so。所以 tbb 分配器应该不是问题。
有人知道我们如何在 worker 空闲时释放内存吗?
在调用 delete
或 free
后,堆分配的内存通常不会返回到 OS。您需要调用 malloc_trim
或特定于分配器的函数来执行此操作。
尽管有连接的分配器,TBB 调度程序仍缓存任务分配,但它不解释 500MB。可以解释的是,TBB 动态加载 TBB 分配器,如果它可以在 libtbb.so
旁边找到它,它当然会缓存内存。您可以通过设置 env var TBB_VERSION=1
来检查 tbbmalloc 是否被激活
我觉得奇怪的是,为什么当 TBB 创建自己的工作线程时,你用你的工作线程超额订阅机器?
我们在重用使用英特尔 TBB 函数的线程时遇到了高内存开销。我们期望一旦线程完成给定的工作负载,它就会释放相应的内存。然而,情况似乎并非如此,即使线程执行工作单元之间有很长的停顿。
我们准备了一个例子来说明问题:
int main() {
blocking_queue<size_t> command_input_queue;
tbb::atomic<size_t> count = 1;
//workers
std::vector<std::thread> worker;
for(size_t i = 0; i < 15; i++) {
worker.push_back(std::thread([&command_input_queue, &count](){
while(true)
{
size_t size;
//wait for work..
command_input_queue.wait_and_pop(size);
//do some work with Intel TBB
std::vector<int32_t> result(size);
for(size_t i = 0; i < result.size(); i++) {
result[i] = i % 1000;
}
tbb::parallel_sort(result.begin(), result.end());
size_t local_count = count++;
std::cout << local_count << " work items executed " << std::endl;
}
}));
}
//enqueue work
size_t work_items = 15;
for(size_t i = 0; i < work_items ; i++) {
command_input_queue.push(10 * 1000 * 1000);
}
while(true) {
boost::this_thread::sleep( boost::posix_time::seconds(1) );
if(count > 15) {
break;
}
}
//wait for more commands
std::cout << "Wait" << std::endl;
boost::this_thread::sleep( boost::posix_time::seconds(60) );
//----!During the wait, while no thread is active,
//the process still claims over 500 MB of memory!----
for(size_t i = 0; i < 15; i++) {
command_input_queue.push(1000 * 1000);
}
...
在示例中我们启动了 15 个工作线程。他们等待任务并执行 tbb::parallel_sort 并在完成后释放所有资源。 问题是在处理完所有任务后,所有工作人员都在等待新任务,该进程仍然占用 500MB 内存。
像 valgrind 的 massif 这样的工具没有告诉我们内存在哪里被声明。 我们将程序链接到 libtbb.so。所以 tbb 分配器应该不是问题。
有人知道我们如何在 worker 空闲时释放内存吗?
在调用 delete
或 free
后,堆分配的内存通常不会返回到 OS。您需要调用 malloc_trim
或特定于分配器的函数来执行此操作。
尽管有连接的分配器,TBB 调度程序仍缓存任务分配,但它不解释 500MB。可以解释的是,TBB 动态加载 TBB 分配器,如果它可以在 libtbb.so
旁边找到它,它当然会缓存内存。您可以通过设置 env var TBB_VERSION=1
我觉得奇怪的是,为什么当 TBB 创建自己的工作线程时,你用你的工作线程超额订阅机器?