Fixing tinyThread memory leak#186
Conversation
Fixing RcppCore#185 Avoid setting `mNotAThread` to true when using pthread to allow threads to join/detach. This will allow the resources to be collected upon termination of threads.
|
Thanks -- I'll have to reserve some time to take a closer look. We use tinythread in a couple other packages (e.g. |
|
I have not had time to dig either but I would caution that we would probably want to see this in valgrind first. (Only saying this because I helped a Rcpp user recently and while I would have to go back and check the details the issue of (casually observed in simulation runs return large result matrices repeatedly) the issue was the creation of lots of string objects which R (as far as I understand it) keeps in a different memory pool so the usual copy-on-write recycling did not hold. Ensuring numeric results only helped. So sometimes the 'leak' can be due to something else.) |
|
@eddelbuettel I understand your concern, and RcppParallel has existed for long time and lots of packages are depending on it. It's better to be conservative when making fundamental changes. |
|
Tl;DR: For those who tracked this far. The memory leak in RcppParallel happens only in certain OSX and Linux. Windows users are not going to observe it. If you are using TBB, this memory leak may not happen as well (I don't have TBB installed) The leak happens even if the worker operator member function does nothing. The amount of memory leak depends on the size of thread-local space, and how many threads get joined before being disregarded, which is small in most cases. However, the performance of the function becomes poor over time. For example, However, as the package creator's noted, the above observation may have just been a false positive due to potential bugs in my script. Please do your own research before using this PR. |
|
Sorry @dipterix; did you intend to close this? |
|
Oh sorry @kevinushey I thought we are not going to fix it so I closed it in case people feel confused. Please feel free to reopen if necessary. |
|
Thanks! Sorry, I still need to carve out time to properly review this but I do intend to... |
kevinushey
left a comment
There was a problem hiding this comment.
This doesn't seem like the right approach to me. If I understand correctly, the real issue is that the threads we're creating with tinythread are never being joined on close; is that correct?
If so, then I think the appropriate patch is probably in how RcppParallel is using tinythread rather than tinythread itself.
| if(joinable()) | ||
| std::terminate(); | ||
| #elif defined(_TTHREAD_POSIX_) | ||
| join(); |
There was a problem hiding this comment.
The C++ standard says (https://en.cppreference.com/w/cpp/thread/thread/~thread):
If *this has an associated thread (joinable() == true), std::terminate() is called.
so I think the behavior here in tinythread was intended to conform with that. In other words, I don't think the destructor should try to join / attach here.
|
Thanks for insightful explanation.
This is kind of correct. The tinythreads are always joined before closing. However, in some situations, the inner pthreads are never being joined nor detached on close, hence resources used inside of the pthreads are not released. Please let me explain. In the following code, after L123 and before L126, RcppParallel/inst/include/RcppParallel/TinyThread.h Lines 120 to 129 in 1a21781 This is because when parallel worker is super fast (fast than pthread ramp-up overhead), some threads will have RcppParallel/inst/include/tthread/tinythread.h Lines 874 to 876 in 1a21781 will alter the Because the RcppParallel/inst/include/tthread/tinythread.h Lines 919 to 930 in 1a21781 (Some comments) Interestingly in In this article, I found the reason why the memory leak was ~10MB on my server: https://developer.ibm.com/tutorials/l-memory-leaks/
|
|
For reference, here's what I see on my Ubuntu 22.04 VM... I think I understand what's going on now. Basically, if you try to call I think this implies we want to allow joining a thread even after it has finished execution -- that is, we should allow calls to |
Fixing #185
Avoid setting
mNotAThreadto true when using pthread to allow threads to join/detach. This will allow the resources to be collected upon termination of threads.The leak was found on
The script has been tested on
Valgrind has been evaluated on with the latest rocker/r-devel-ubsan-clang under docker container (host OSX x86)
R -d "valgrind --tool=memcheck --leak-check=full" tmp.Rtmp.Rhas been given in the issue #185The memory check result:
still reachableexists even if I do nothing at all so I think it's fixed? No indirectly lost nor possibly lost was reported