Pandas: рдЪрдХреНрд░реАрдп рдЬреАрд╕реА рдореБрджреНрджреЗ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 8 рдЬрдире░ 2013  ┬╖  14рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: pandas-dev/pandas

рдЬрд▓реНрдж рд╣реА рдЦрд╝рддреНрдо рд╣реЛрдиреЗ рд╡рд╛рд▓рд╛ рдПрдХ рд░рд╣рд╕реНрдп:

import pandas as pd
import numpy as np

arr = np.random.randn(100000, 5)

def leak():
    for i in xrange(10000):
        df = pd.DataFrame(arr.copy())
        result = df.xs(1000)
        # result = df.ix[5000]

if __name__ == '__main__':
    leak()

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рд░рд┐рдХреЙрд░реНрдб рдХреЗ рд▓рд┐рдП, рд╣рдо (+ @ sbneto) рдереЛрдбрд╝реЗ рд╕рдордп рдХреЗ рд▓рд┐рдП рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рдФрд░ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ:

# monkeypatches.py

# Solving memory leak problem in pandas
# https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
import pandas as pd
from ctypes import cdll, CDLL
try:
    cdll.LoadLibrary("libc.so.6")
    libc = CDLL("libc.so.6")
    libc.malloc_trim(0)
except (OSError, AttributeError):
    libc = None

__old_del = getattr(pd.DataFrame, '__del__', None)

def __new_del(self):
    if __old_del:
        __old_del(self)
    libc.malloc_trim(0)

if libc:
    print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
    pd.DataFrame.__del__ = __new_del
else:
    print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)

рд╕рднреА 14 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдареАрдХ рд╣реИ, рдпрд╣ рдПрдХ рд╢рдмреНрдж рдореЗрдВ, рдЪ * cked рд╣реИред рдЕрдЧрд░ рдореИрдВ рд▓реВрдк рдХреЗ рд▓рд┐рдП gc.collect рдЬреЛрдбрд╝рддрд╛ рд╣реВрдВ рддреЛ рдпрд╣ рдореЗрдореЛрд░реА рдХреЛ рд▓реАрдХ рдХрд░рдирд╛ рдмрдВрдж рдХрд░ рджреЗрддрд╛ рд╣реИ:

import pandas as pd
import numpy as np
import gc

arr = np.random.randn(100000, 5)

def leak():
    pd.util.testing.set_trace()
    for i in xrange(10000):
        df = pd.DataFrame(arr.copy())
        result = df.xs(1000)
        gc.collect()
        # result = df.ix[5000]

if __name__ == '__main__':
    leak()

рдпрд╣рд╛рдВ рдРрд╕реА рд╡рд╕реНрддреБрдПрдВ рд╣реИрдВ рдЬреЛ рдЪрдХреНрд░реАрдп рдЬреАрд╕реА рдЪрд▓рдиреЗ рдкрд░ рдХреЗрд╡рд▓ рдХрдЪрд░рд╛ рдПрдХрддреНрд░ рдХрд░рддреА рд╣реИрдВред рдпрд╣рд╛рдБ рдХреНрдпрд╛ рдЙрдкрд╛рдп рд╣реИ, __del__ рдореЗрдВ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЪрдХреНрд░ рдХреЛ рддреЛрдбрд╝реЗрдВ рддрд╛рдХрд┐ рдкрд╛рдпрдерди рдореЗрдореЛрд░реА рдПрд▓реЛрдХреЗрдЯрд░ рд╣рдореЗрдВ рдбрд░рд╛рддрд╛ рд╣реИ?

рдХреНрдпрд╛ рдЖрдк рдпрд╣ рдХреЛрд╢рд┐рд╢ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

from ctypes import cdll, CDLL

import pandas as pd
import numpy as np

arr = np.random.randn(100000, 5)

cdll.LoadLibrary("libc.so.6")
libc = CDLL("libc.so.6")

def leak():
    for i in xrange(10000):
        libc.malloc_trim(0)
        df = pd.DataFrame(arr.copy())
        result = df.xs(1000)
        # result = df.ix[5000]

if __name__ == '__main__':
    leak()

рдореБрдЭреЗ рд╕рдВрджреЗрд╣ рд╣реИ рдХрд┐ рдЗрд╕рдХрд╛ рдЕрдЬрдЧрд░ рд╕реЗ рдХреЛрдИ рд▓реЗрдирд╛-рджреЗрдирд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЗрд╕рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдЧрд╛ред

рд╣рд╛рдБ, рдпрд╣ рдЪрд╛рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд▓рдЧ рд░рд╣рд╛ рдерд╛ред рд╕реНрдореГрддрд┐ рдЙрдкрдпреЛрдЧ 450MB рдХрд┐ IPython рдореЗрдВ рдЪрд▓рд╛рдиреЗ рдХреЗ рдмрд╛рдж, рддреЛ malloc_trim рдиреЗ 400MB рдХреЛ рдореБрдХреНрдд рдХрд░ рджрд┐рдпрд╛ред рдмрд╣реБрдд рдЕрд╢реБрдн

malloc_trim рд▓реАрдб рдЕрдкрд╕реНрдЯреНрд░реАрдо рдХреЗ рдмрд╛рдж, рдпрд╣ рдПрдХ glibc рдСрдкреНрдЯрд┐рдорд╛рдЗрдЬрд╝реЗрд╢рди рдХреА рддрд░рд╣ рд▓рдЧ рд░рд╣рд╛ рд╣реИред
xref:
http://sourceware.org/bugzilla/show_bug.cgi?id=14827

"fastbins" рдЯрд┐рдкреНрдкрдгреА рджреЗрдЦреЗрдВред

In [1]: from ctypes import Structure,c_int,cdll,CDLL
   ...: class MallInfo(Structure):   
   ...:     _fields_ =[
   ...:               ( 'arena',c_int ),  #  /* Non-mmapped space allocated (bytes) */
   ...:            ('ordblks',c_int  ),# /* Number of free chunks */
   ...:            (    'smblks',c_int ),  # /* Number of free fastbin blocks */
   ...:            (    'hblks',c_int  ),  #/* Number of mmapped regions */
   ...:            (    'hblkhd' ,c_int ), #/* Space allocated in mmapped regions (bytes) */
   ...:            (    'usmblks' ,c_int), # /* Maximum total allocated space (bytes) */
   ...:            (    'fsmblks' ,c_int) ,#/* Space in freed fastbin blocks (bytes) */
   ...:            (    'uordblks' ,c_int),# /* Total allocated space (bytes) */
   ...:            (    'fordblks',c_int ),# /* Total free space (bytes) */
   ...:            (    'keepcost',c_int )# /* Top-most, releasable space (bytes) */
   ...:          ]
   ...:     def __repr__(self):
   ...:         return "\n".join(["%s:%d" % (k,getattr(self,k)) for k,v in self._fields_])
   ...: 
   ...: cdll.LoadLibrary("libc.so.6")
   ...: libc = CDLL("libc.so.6")
   ...: mallinfo=libc.mallinfo
   ...: mallinfo.restype=MallInfo
   ...: libc.malloc_trim(0)
   ...: mallinfo().fsmblks
Out[1]: 0

In [2]: import numpy as np
   ...: import pandas as pd
   ...: arr = np.random.randn(100000, 5)
   ...: def leak():
   ...:     for i in xrange(10000):
   ...:         df = pd.DataFrame(arr.copy())
   ...:         result = df.xs(1000)
   ...: leak()
   ...: mallinfo().fsmblks
Out[2]: 128

In [3]: libc.malloc_trim(0)
   ...: mallinfo().fsmblks
Out[3]: 0

рддрдм рдареАрдХ рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рд╣рдореЗрдВ рдХрд┐рд╕реА рджрд┐рди рдкрдВрдбреЛрдВ рдХреЛ рдорд╛рд▓рдХрд┐рди рдЯреНрд░рд┐рдорд┐рдВрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рд╕рд╣рд╛рдпрдХ рдХрд╛рд░реНрдп рдХрд░рдиреЗ рдЪрд╛рд╣рд┐рдП

рдЕрдХреНрд╕рд░ рдкреВрдЫреЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рд╢реНрди рдореЗрдВ рдкреНрд░рд╡реЗрд╢, рд╢рд╛рдпрдж?

рд░рд┐рдХреЙрд░реНрдб рдХреЗ рд▓рд┐рдП, рд╣рдо (+ @ sbneto) рдереЛрдбрд╝реЗ рд╕рдордп рдХреЗ рд▓рд┐рдП рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рдФрд░ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ:

# monkeypatches.py

# Solving memory leak problem in pandas
# https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
import pandas as pd
from ctypes import cdll, CDLL
try:
    cdll.LoadLibrary("libc.so.6")
    libc = CDLL("libc.so.6")
    libc.malloc_trim(0)
except (OSError, AttributeError):
    libc = None

__old_del = getattr(pd.DataFrame, '__del__', None)

def __new_del(self):
    if __old_del:
        __old_del(self)
    libc.malloc_trim(0)

if libc:
    print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
    pd.DataFrame.__del__ = __new_del
else:
    print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)

@alanjds рдмрд╣реБрдд рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж!

рд▓реЗрдХрд┐рди рдЕрдиреНрдп рдкреНрд░рднрд╛рд╡рд┐рдд рдСрдкрд░реЗрд╢рди рд╣реИрдВ :-(

рдпрд╣ рдмрд╣реБрдд рдЕрдЬреАрдм рд╣реИ рдХрд┐ рдКрдкрд░ рдЬрд╛рд░реА (glibc рдХрд╛ рдореБрджреНрджрд╛) рдХреЛрдИ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдирд╣реАрдВ рд╣реИред рдпрд╣ рд▓рд┐рдирдХреНрд╕ рдкреАрд╕реА рдФрд░ рд╕рд░реНрд╡рд░ рдХреЗ рд╕рднреА рд╡рд╛рддрд╛рд╡рд░рдг рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИред рдФрд░ рдХреБрдЫ рдирд╣реАрдВ!!!

рдореБрдЭреЗ рдкрддрд╛ рд╣реИ, рдЖрдк рдореБрдЭреЗ рдХрд╣реЗрдВрдЧреЗ: рдареАрдХ рд╣реИ, рдПрдХ рдкреИрдЪ рд▓рд┐рдЦреЗрдВ! рдореИрдВ рдЗрд╕реЗ рдХрд░реВрдБрдЧрд╛ (UPD: рд▓реЗрдХрд┐рди рдпрд╣ рдЕрдЬреАрдм рдХрд╛рд░рдг рд╣реЛрдЧрд╛ рдХреНрдпреЛрдВрдХрд┐ рдореБрдЭреЗ glibc рдХреЛрдб рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреБрдЫ рдирд╣реАрдВ рдкрддрд╛ рд╣реИ)ред рд▓реЗрдХрд┐рди рдпрд╣ рднреА рдХрд┐рд╕реА рдХреЛ рдкрддрд╛ рдирд╣реАрдВ рд╣реИред

рд╣рд░ рдХреЛрдИ рдХрд╣рддрд╛ рд╣реИ: рдХреЗрдбреАрдИ рд▓реАрдХред рдХреМрди рдЬрд╛рдирддрд╛ рд╣реИ - рдХреНрдпреЛрдВ ?! рдХреЛрдИ рднреА рдирд╣реАрдВ!

рдЦреБрд▓рд╛ рд╕реНрддреНрд░реЛрдд? рд╢рд░реНрдо рдХреА рдмрд╛рдд рд╣реИ! рдХреНрд╖рдорд╛ рдХрд░реЗрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдХреЗ рд▓рд┐рдП рд╕рд╣реА рд╣реИред

PS http://sourceware.org/bugzilla/show_bug.cgi?id=14827

рдореБрдЭреЗ рдЖрдк рдкрд░ рд╡рд┐рд╢реНрд╡рд╛рд╕ рд╣реИред 2 рд╕рд╛рд▓ рдФрд░ рдЙрд╕ рддрд░рдл рдХреЛрдИ рдХрджрдо рдирд╣реАрдВ: /

рдореИрдВ рдЗрд╕ рдкрдХреНрд╖ рдХреЛ рдареАрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рддрд╛ рд╣реВрдВ рдФрд░ рджреЛрд╖ рдХреА рдПрдХ рдмрдбрд╝реА рдЯрд┐рдкреНрдкрдгреА рдХрд░рддрд╛ рд╣реВрдВ, рдХреНрдпреЛрдВрдХрд┐ рд╡рд╣рд╛рдВ рдлреЛрд░реНрдХрд┐рдВрдЧ рдЕрдХреНрд╖рдореНрдп рд▓рдЧрддреА рд╣реИред

@alanjds рдЖрдкрдХреЗ рдХреЛрдб рдиреЗ рдореЗрд░реЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╕реНрдпрд╛ рддрдп рдХрд░ рджреА рд╣реИ рдЬреЛ рдПрдХ рдкреНрд░рдореБрдЦ рд╕рд┐рд░рджрд░реНрдж рдХрд╛ рдХрд╛рд░рдг рдмрди рд░рд╣реА рдереАред рдХреНрдпрд╛ рдЖрдк рдпрд╣ рдмрддрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рддреИрдпрд╛рд░ рд╣реИрдВ рдХрд┐ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдкрд╛рдВрдбрд╛ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреНрдпрд╛ рд╣реИ рдФрд░ рдЖрдкрдХрд╛ рдХреЛрдб рдЗрд╕реЗ рдХреИрд╕реЗ рдареАрдХ рдХрд░рддрд╛ рд╣реИ?

рдЖрдк рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рдЕрдкрдиреЗ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдЖрд╡рдВрдЯрдирдХрд░реНрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ jemalloc рд╕реНрд╡рд┐рдЪ рдХрд░рдХреЗ рднреА рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдХреЗ рдмрдЬрд╛рдп python script.py , рдЪрд▓рд╛рдиреЗ LD_PRELOAD=/usr/lib/libjemalloc.so python script.py ред рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ libjemalloc.so рдХрд╛ рдкрде рдЖрдкрдХреЗ рд╕рд┐рд╕реНрдЯрдо рдкрд░ рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдкрд╣рд▓реЗ рдЗрд╕реЗ рдЕрдкрдиреЗ рдкреИрдХреЗрдЬ рдореИрдиреЗрдЬрд░ рдХреЗ рд╕рд╛рде рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

@tchristensenowlet рд╕рдорд╕реНрдпрд╛ malloc glibc рдХреЛрдб рдореЗрдВ рд▓рдЧрддреА рд╣реИред рдЬрд╛рд╣рд┐рд░рд╛ рддреМрд░ рдкрд░, free рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╡рд╣рд╛рдБ рдПрдХ рдзреНрд╡рдЬ рдХрд╛ рд╕рдореНрдорд╛рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕реАрдорд╛ рдХреЗ рдмрд╛рдж malloc_trim рдЬрд╛рд░реА рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП, рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк @ghost рдХреЗ рд▓рд┐рдВрдХ рдореЗрдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, malloc_trim рдХреЛ рдХрднреА рднреА рдореЗрдореЛрд░реА рд▓реАрдХ рдирд╣реАрдВ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред рд╣рдордиреЗ рдЬреЛ рдХрд┐рдпрд╛ рд╡рд╣ рдмрд╕ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ malloc_trim рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИ, рдпрджрд┐ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдЙрдкрд▓рдмреНрдз рд╣реИред рд╣рдо рдЗрд╕реЗ __del__() рд╡рд┐рдзрд┐ рдореЗрдВ рдХрд╣рддреЗ рд╣реИрдВ, рдЬрд┐рд╕реЗ рддрдм рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрдм рдСрдмреНрдЬреЗрдХреНрдЯ рдХрдЪрд░рд╛ рдПрдХрддреНрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

glibc.malloc.mxfast tunable рдХреЛ Glibc (https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html) рдореЗрдВ рдкреЗрд╢ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рд╣рдорд╛рд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рд╕реЗ рдПрдХ рдореЗрдВ рдЕрдкрд░рд╛рдзреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдкрд╛рдпрдерди 3.8 (рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рд╡реЗрдмрд╕рд╛рдЗрдЯ рд╕реЗ) рдФрд░ рдкрд╛рдЗрдк рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реНрдерд╛рдкрд┐рдд рд╕рднреА рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд╕рд╛рде рд╡рд┐рдВрдбреЛрдЬ рдЪрд▓рд╛ рд░рд╣реЗ рд╣реИрдВред рдХреНрдпрд╛ рдпрд╣ рд╕рдорд╕реНрдпрд╛ рд╡рд┐рдВрдбреЛрдЬ рдкрд░ рднреА рд╣реЛрдЧреА? рдпрджрд┐ рд╣рд╛рдВ, рддреЛ cdll.LoadLibrary("libc.so.6") рд╕рдорддреБрд▓реНрдп рдХреНрдпрд╛ рд╣реЛрдЧрд╛?

рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ: рдореИрдВрдиреЗ рдпрд╣рд╛рдВ рд╡рд░реНрдгрд┐рдд рдЗрди рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдЪрд▓рд╛рдпрд╛, рдФрд░ рдПрдХрддреНрд░ рдХрд┐рдП рдЧрдП рдХрдЪрд░реЗ рдиреЗ рд╣рд░ рдмрд╛рд░ рдЕрдкрдирд╛ рдХрд╛рдо рдареАрдХ рд╕реЗ рдХрд┐рдпрд╛:
https://github.com/pandas-dev/pandas/issues/21353
рд╕рд┐рд╕реНрдЯрдо: рд╡рд┐рдВрдбреЛрдЬ 10
рдкрд╛рдпрдерди: 3.8.5
рдкрдВрдбреЛрдВ: 1.1.0

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
1 / 5 - 1 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

mfmain picture mfmain  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

andreas-thomik picture andreas-thomik  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

ericdf picture ericdf  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

scls19fr picture scls19fr  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

MatzeB picture MatzeB  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ