-
-
Notifications
You must be signed in to change notification settings - Fork 25.8k
NearestNeighbors radius_neighbors memory leaking #11051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@recamshak do you happen to be aware of this? @EemeliSaari have you checked this is present in master? This was recently changed. |
The memoryerror for brute is to be expected until #10280 is merged
|
The issue was with the leaking memory with the other algorithms. Edited: Also I'm working with the latest release version of the sklearn so that would be no to the master branch question. |
yes, I'm aware, and that is what you should please test on master
|
No changes to the memory usage when running the test script with the master branch. Might this be a problem only on my machine? |
I can reproduce on master using Linux and Python 3.6 for both import gc
from sklearn.neighbors import NearestNeighbors
import numpy as np
from memory_profiler import memory_usage
def get_memory_usage():
return memory_usage(timeout=0.2)[0]
print('Initial memory usage: %.2f MB' % get_memory_usage())
X = np.random.rand(20000, 2) # Random data
print('Allocated X: %.2f MB' % get_memory_usage())
print('X.nbytes == %.2f MB' % (X.nbytes / 1e6))
neighbors_model = NearestNeighbors(algorithm='ball_tree')
neighbors_model.fit(X)
print('Fitted NearestNeighbors: %.2f MB' % get_memory_usage())
neighborhoods = neighbors_model.radius_neighbors(X, 0.5, return_distance=False)
print('Computed radius_neighbors: %.2f MB' % get_memory_usage())
del neighborhoods
del neighbors_model
del X
gc.collect()
print('Unallocated everything: %.2f MB' % get_memory_usage()) produces,
|
It's also the case in 0.18.1 and 0.19.1. Seems fairly credible given the widespread use of C pointers in the kd / ball tree implementation. After a quick look I wasn't able to find the issue, but given the amount of memory leaked there shouldn't be that many objects that need to allocate 1.5 GB memory in this example. In particular, it seems to be of the order of Furthermore, calling
|
@jnothman I am not aware of any memory leak, but I'll check my MR. |
It seems that setting the |
hmm... but that should affect master and NOT existing releases, so it might
be a separate issue
|
On 0.19.1 I got:
|
Yes, previously I have built 0.18.1 and 0.19.1 from sources with Python 3.6, Cython 0.25.2, Numpy 1.12.1, scipy 0.19.0 (installed with conda), where I can reproduce this issue. On the other hand, using either 0.18.1 / 0.19.1 conda binary files or manylinux wheels from PyPi seems fine for me as well. These would use the latest Numpy 1.14.1, scipy 1.0.1. Maybe I'm missing something with my local builds, but in any case @EemeliSaari was also seeing this issue for 0.19.1 not on master, so normally it should be unrelated to #10887 |
Sorry, I shouldn't have let #11056 close this. I think that solved a separate issue. |
In an Ubuntu xenial container with idential parameters to OP, I can reproduce on master (after the #11056 fix) but not in 0.19.1, using the same benchmark script as above and the following setup, docker run --rm ubuntu:xenial-20180417 /bin/bash -c "
apt-get update &&
apt-get install -yq libatlas-dev libatlas-base-dev liblapack-dev gfortran python3-pip wget git &&
pip3 install numpy scipy Cython==0.25.2 memory_profiler psutil &&
# adapt the git tag as needed
pip3 install git+https://github.com/scikit-learn/scikit-learn.git@master &&
# run the benchmark script
wget -O - https://github.com/scikit-learn/scikit-learn/files/1970318/test_memory_leak.txt | python3 -
" |
On a slightly modified benchmark script on master you can see that the memory get reused:
On a Windows 10 Home system, the same script gives:
Is it expected from malloc/free to not directly release the memory to the OS in certain case? Not sure why then 0.19.1 and master don't give the same memory usage. Maybe numpy is using a different memory allocator? As far as I can tell it's using So I tried with the following change on master to make sure I use the same allocator, but it has no effect: diff --git a/sklearn/neighbors/binary_tree.pxi b/sklearn/neighbors/binary_tree.pxi
index edf78257c..3d5986ec4 100755
--- a/sklearn/neighbors/binary_tree.pxi
+++ b/sklearn/neighbors/binary_tree.pxi
@@ -144,7 +144,6 @@
cimport cython
cimport numpy as np
from libc.math cimport fabs, sqrt, exp, cos, pow, log
-from libc.stdlib cimport calloc, malloc, free
from libc.string cimport memcpy
from sklearn.utils.lgamma cimport lgamma
@@ -161,6 +160,11 @@ from dist_metrics cimport (DistanceMetric, euclidean_dist, euclidean_rdist,
cdef extern from "numpy/arrayobject.h":
void PyArray_ENABLEFLAGS(np.ndarray arr, int flags)
+cdef extern from "Python.h":
+ void* malloc(int size) nogil
+ void* calloc(int num, int size) nogil
+ void free(void *p) nogil
+
np.import_array()
# some handy constants |
I can't remember the terminology, but I recall previously identifying similar behaviour - memory being kept for reuse - as a Linux (I think) feature that made it look like memory consumption was increasing when it wasn't. |
This sounds relevant, https://www.linuxquestions.org/questions/programming-9/how-to-free-memory-immediately-after-calling-free-in-c-programming-916896/#post4540924
But then if this is true, I don't understand why it's not an issue when benchmarking memory usage in general... In case it's relevant, in the benchmark script, |
Maybe @aabadie would have some ideas about this ? |
After digging into glibc I found that the memory manager behavior can be changed via environment variables. See the end of I tried to run
It's significantly slower though. 12s instead of 9s. So it seems that there is actually no memory leak. But as @rth said, I don't understand why the behavior is different whether we do the |
Thanks @EemeliSaari for helping us find a different issue, but I think we will close unless this cab be verified more reliably |
Thanks for investigating @recamshak !
Interesting, in particular,
sounds also relevant, but I can't wrap my head around it (and I'm not sure I want to)... For the record, I also tried running a more in depth memory benchmark with all fieds from def get_memory_usage():
import psutil
res = psutil.Process().memory_full_info()
return ' '.join('%s: %.3f MB' % (key, val/2**20)
for key, val in res._asdict().items() if val) (and adapting the print statements in the benchmark script) which produces,
The original benchmark script only shows the RSS memory, but in the end I don't think this provides any additional insight. I agree with @jnothman about closing this because,
|
Description
NearestNeighbors uses a large chunck of memory in run time without releasing it even after calling
del
or assigning a empty array to the object variable. The memory will how ever be released after the python process is terminated.I noticed the bug when I was trying to fit a
DBSCAN
model and by looking deeper into the issue I was able to reproduce the same memory leak with a random data by usingNearestNeighbors.radius_neighbors()
method.The memory leaked in a 12GB RAM machine was:
10%
.Steps/Code to Reproduce
The algorithm modes that were producing the leak were:
With the algorithm brute the memory leak didn't happen but when increasing the data size by factor of 10 the following error happened
Expected Results
Versions
The text was updated successfully, but these errors were encountered: