Intel® oneAPI Threading Building Blocks
Ask questions and share information about adding parallelism to your applications when using this threading library.

memory leak from parallel for?

rnickb
Beginner
1,648 Views

Clang's address sanitizer detects a memory leak in TBB when this program is run

#include <tbb/parallel_for.h>                                                    
#include <vector>                                                                
#include <cmath>                                                                 
                                                                                 
template <class T>                                                               
void do_not_optimize_away(T&& x) {                                               
  asm volatile("" : "+r"(x));                                                    
}                                                                                
                                                                                 
const int N = 100'000;                                                           
void f() {                                                                       
  std::vector<double> v(N);                                                      
  for (int i = 0; i < N; ++i) v = std::sqrt(i);                               
  do_not_optimize_away(v.data());                                                
}                                                                                
                                                                                 
int main() {                                                                     
  tbb::parallel_for(0, 100, [](int i) { f(); });                                 
  return 0;                                                                      
} 

The report I get is

=================================================================
==17071==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 3096 byte(s) in 3 object(s) allocated from:
    #0 0x4cb25b in operator new[](unsigned long) /root/clang-march30_15/llvm/projects/compiler-rt/lib/asan/asan_new_delete.cc:64:37
    #1 0x7f14f5649e12 in tbb::internal::task_stream<3>::initialize(unsigned int) /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/task_stream.h:94
    #2 0x7f14f56462f3 in tbb::internal::arena::arena(tbb::internal::market&, unsigned int) /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/arena.cpp:185
    #3 0x7f14f5646436 in tbb::internal::arena::allocate_arena(tbb::internal::market&, unsigned int) /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/arena.cpp:201
    #4 0x7f14f5642eab in tbb::internal::market::create_arena(int, unsigned long, bool) /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/market.cpp:234
    #5 0x7f14f56417d2 in tbb::internal::governor::init_scheduler(int, unsigned long, bool) /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/governor.cpp:193
    #6 0x7f14f563eac7 in tbb::internal::governor::local_scheduler() /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/governor.h:133
    #7 0x7f14f563e380 in tbb::internal::get_initial_auto_partitioner_divisor() /root/tbb/tbb44_20150728oss/build/linux_intel64_gcc_cc4.7.3_libc2.17_kernel3.10.25_debug/../../src/tbb/task.cpp:157
    #8 0x4d1d11 in tbb::interface7::internal::adaptive_partition_type_base<tbb::interface7::internal::auto_partition_type>::adaptive_partition_type_base() (/home/rnburn/bugs/tbb_leak/a.out+0x4d1d11)
    #9 0x4d1c1f in tbb::interface7::internal::auto_partition_type::auto_partition_type(tbb::auto_partitioner const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4d1c1f)
    #10 0x4ccdc1 in tbb::interface7::internal::start_for<tbb::blocked_range<int>, tbb::internal::parallel_for_body<main::$_0, int>, tbb::auto_partitioner const>::start_for(tbb::blocked_range<int> const&, tbb::internal::parallel_for_body<main::$_0, int> const&, tbb::auto_partitioner const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4ccdc1)
    #11 0x4ccc18 in tbb::interface7::internal::start_for<tbb::blocked_range<int>, tbb::internal::parallel_for_body<main::$_0, int>, tbb::auto_partitioner const>::run(tbb::blocked_range<int> const&, tbb::internal::parallel_for_body<main::$_0, int> const&, tbb::auto_partitioner const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4ccc18)
    #12 0x4cca24 in void tbb::parallel_for<tbb::blocked_range<int>, tbb::internal::parallel_for_body<main::$_0, int> >(tbb::blocked_range<int> const&, tbb::internal::parallel_for_body<main::$_0, int> const&, tbb::auto_partitioner const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4cca24)
    #13 0x4cc7d8 in void tbb::strict_ppl::parallel_for_impl<int, main::$_0, tbb::auto_partitioner const>(int, int, int, main::$_0 const&, tbb::auto_partitioner const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4cc7d8)
    #14 0x4cc405 in void tbb::strict_ppl::parallel_for<int, main::$_0>(int, int, main::$_0 const&) (/home/rnburn/bugs/tbb_leak/a.out+0x4cc405)
    #15 0x4cc291 in main (/home/rnburn/bugs/tbb_leak/a.out+0x4cc291)
    #16 0x7f14f474db94 in __libc_start_main (/lib64/libc.so.6+0x24b94)

SUMMARY: AddressSanitizer: 3096 byte(s) leaked in 3 allocation(s).

When I compiled with

clang++ -std=c++14 -fsanitize=address -ltbb_debug main.cpp

using clang 3.6

False positives with address sanitizer should be very rare (http://clang.llvm.org/docs/AddressSanitizer.html). And I tried adding some print statements and it looks like the destructor for task_stream may not have been called.

0 Kudos
8 Replies
RafSchietekat
Valued Contributor III
1,649 Views

What happens if you put an explicit "tbb::task_scheduler_init anonymous;" just before the call to parallel_for()?

0 Kudos
rnickb
Beginner
1,649 Views

there's no reported memory leak with "tbb::task_scheduler_init anonymous;"

0 Kudos
Vladimir_P_1234567890
1,649 Views

rnickb wrote:

there's no reported memory leak with "tbb::task_scheduler_init anonymous;"

That means that if you use scoped initialization, all odjects are destroyed in the end of the scope. If you use autoinitialization (without tbb::task_scheduler_init) then created objects live till the end of the program. So it looks that everything works correctly

--Vladimir

0 Kudos
rnickb
Beginner
1,649 Views

Can't they be deleted at the end of the program so that it doesn't flag as a memory leak?

0 Kudos
Vladimir_P_1234567890
1,649 Views

Yes they can. You just need to construst this object and destruct it like it is done for other C++ objects.

0 Kudos
rnickb
Beginner
1,649 Views

Why can't the library guarantee the destructors are called, irrespective of whether task_scheduler_init object is created or not so that all the allocated heap memory gets cleanly deleted?

0 Kudos
Vladimir_P_1234567890
1,649 Views

Upon exit OS kills worker threads where destructors are located so it is impossible to call destructors in this case. if you use C++ style for creating/deleting objects then destructors are called.

If lazy (auto) initialization is used I do not see any issues with fact that lazy de-initialization is used. And de-initialization is so lazy that it is done by an operating system.:)

--Vladimir

0 Kudos
Roman_P_Intel
Employee
1,649 Views

task_scheduler_init has been deprecated. What is the current BKM to use TBB with ASAN?

0 Kudos
Reply