곧 λλ²κΉ λ λ―Έμ€ν°λ¦¬ :
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()
μ’μ, μ΄κ²μ νλ§λλ‘ λ§ν κ±°μΌ. for 루νμ 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()
μ¬κΈ°μλ μν GCκ° μ€νλ λλ§ κ°λΉμ§ μμ§λλ κ°μ²΄κ° μμ΅λλ€. μ΄λ€ μ루μ
μ΄ ν΄μμ£ΌκΈ°κ° λͺ
μ μ μΌλ‘, μ¬κΈ°μ __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()
λλ μ΄κ²μ΄ νμ΄μ¬κ³Ό μλ¬΄λ° κ΄λ ¨μ΄ μλ€κ³ μκ°νμ§λ§ κ·Έκ²μ κ·Έκ²μ νμΈν κ²μ λλ€.
κ·Έλ, κ·Έκ² μμμλ₯Ό μ°λ κ² κ°μ μ΄. λ©λͺ¨λ¦¬ μ¬μ©λμ IPythonμμ μ€ν ν ν 450MBμ΄κ³ malloc_trimμ 400MBλ₯Ό ν΄μ νμ΅λλ€. λ§€μ° μ μ±
malloc_trim
리λ μ
μ€νΈλ¦Όμ μ΄μ΄ glibc μ΅μ νκ° μλͺ» λ κ²μ²λΌ 보μ
λλ€.
μΈλΆ μ°Έμ‘° :
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
κ·Έλ κ³ μΉμ§ μμ κ²μ λλ€. μλ§λ μ°λ¦¬λ malloc νΈλ¦¬λ°μ μννκΈ° μν΄ μΈμ κ° ν¬λμ λͺ κ°μ§ λμ°λ―Έ ν¨μλ₯Ό μΆκ°ν΄μΌ ν κ²μ λλ€.
FAQ νλͺ©μΌκΉμ?
κΈ°λ‘μ μν΄, μ°λ¦¬ (+ @ 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 λ¬Έμ )μ μλ¬΄λ° λ°μμ΄ μλ€λ κ²μ λ§€μ° μ΄μν©λλ€. Linux PC λ° μλ²μ λͺ¨λ νκ²½μ μν₯μ λ―ΈμΉ©λλ€. κ·Έλ¦¬κ³ μ무κ²λ!!!
μμμ, λΉμ μ μ μκ² λ§ν κ²μ λλ€ : μ’μμ, ν¨μΉλ₯Ό μμ±νμΈμ! λλ κ·Έκ²μ ν κ²μ λλ€ (UPD : glibc μ½λμ λν΄ μ무κ²λ λͺ¨λ₯΄κΈ° λλ¬Έμ μ΄μ ν κ²μ λλ€). κ·Έλ¬λ μ무λ κ·Έκ²μ λͺ¨λ¦ λλ€.
λͺ¨λκ° λ§ν©λλ€ : KDEκ° λμΆλ©λλ€. λκ° μμ-μ?! μ무λ!
μ€ν μμ€? λΆλλ¬μ! μ£μ‘νμ§λ§μ΄ μν©μ μ¬μ€μ λλ€.
λλ λΉμ μ λ―Ώμ΅λλ€. 2 λ μ΄κ³ κ·Έμͺ½μΌλ‘ μμ§μ΄μ§ μλλ€ : /
λλ μ΄μͺ½μ κ³ μΉκ³ κ±°κΈ°μμ ν¬ν¬νλ κ²μ μ€ν λΆκ°λ₯ ν΄ λ³΄μ΄κΈ° λλ¬Έμ ν° λΉλμνκ² λ€κ³ λ§νλ€.
@alanjds κ·νμ μ½λλ μ¬κ°ν λν΅μ μΌμΌν€λ λ¬Έμ λ₯Ό ν΄κ²°νμ΅λλ€. κΈ°λ³Έ pandas λμμ΄ λ¬΄μμ΄λ©° μ½λμμμ΄λ₯Ό μμ νλ λ°©λ²μ μ€λͺ ν΄ μ£Όμκ² μ΅λκΉ?
κΈ°λ³Έ ν λΉ μλ‘ jemalloc
λ‘ μ ννμ¬μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν μλ μμ΅λλ€. λμ python script.py
, μ€ν LD_PRELOAD=/usr/lib/libjemalloc.so python script.py
. libjemalloc.so
μ κ²½λ‘λ μμ€ν
μ λ°λΌ λ€λ₯Ό μ μμΌλ©° λ¨Όμ ν¨ν€μ§ κ΄λ¦¬μλ₯Ό μ¬μ©νμ¬ μ€μΉν΄μΌν©λλ€.
@tchristensenowlet λ¬Έμ λ glibc
μ malloc
μ½λμμλ κ² κ°μ΅λλ€. λΆλͺ
ν free
ꡬνμ @ghost μ λ§ν¬μμ λ³Ό μ μλ―μ΄ νΉμ μκ³ κ° μ΄νμ malloc_trim
λ°νν΄μΌνλ νλκ·Έλ₯Ό μ‘΄μ€νμ§ μμ΅λλ€. λ°λΌμ malloc_trim
λ νΈμΆλμ§ μκ³ λ©λͺ¨λ¦¬ λμκ° λ°μν©λλ€. μμ€ν
μμ libλ₯Ό μ¬μ©ν μμλ κ²½μ° μλμΌλ‘ malloc_trim
νΈμΆνμ΅λλ€. κ°μ²΄κ° κ°λΉμ§ μμ§ λ λ μ€νλλ __del__()
λ©μλμμ νΈμΆν©λλ€.
glibc.malloc.mxfast
νλ λΈμ΄ Glibc (https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html)μ λμ
λμμ΅λλ€.
μ΄κ²μ΄ μ°λ¦¬ νλ‘μ νΈ μ€ νλμ λ²μΈμ΄λΌκ³ μκ°νμ§λ§ μ¬μ©μλ κΈ°λ³Έ Python 3.8 (곡μ μΉ μ¬μ΄νΈμμ μ 곡)κ³Ό pipλ₯Ό ν΅ν΄ μ€μΉλ λͺ¨λ μ’
μμ±μΌλ‘ Windowsλ₯Ό μ€ννκ³ μμ΅λλ€. μ΄ λ¬Έμ κ° Windowsμμλ λ°μν©λκΉ? κ·Έλ λ€λ©΄ cdll.LoadLibrary("libc.so.6")
ν΄λΉνλ κ²μ 무μμ
λκΉ?
νΈμ§ : μ¬κΈ°μ μ€λͺ
λ ν
μ€νΈλ₯Ό μ€ννκ³ κ°λΉμ§ μμ§μ λ§€λ² μ λλ‘ μλνμ΅λλ€.
https://github.com/pandas-dev/pandas/issues/21353
μμ€ν
: Windows 10
νμ΄μ¬ : 3.8.5
νλ€ : 1.1.0
κ°μ₯ μ μ©ν λκΈ
κΈ°λ‘μ μν΄, μ°λ¦¬ (+ @ sbneto)λ μ΄κ²μ μ½κ°μ μκ° λμ μκ°μΌλ‘ μ¬μ©νκ³ μμΌλ©° λ§€μ° μνκ³ μμ΅λλ€.