Sebuah misteri yang akan segera di-debug:
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()
Ok, ini, singkatnya, f * cked up. Jika saya menambahkan gc.collect ke itu untuk loop berhenti membocorkan memori:
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()
Ada objek di sini yang hanya mengumpulkan sampah saat GC siklik berjalan. Apa solusinya di sini, hentikan siklus secara eksplisit di __del__
sehingga pengalokasi memori Python berhenti meniduri kita?
Bisakah Anda mencoba ini:
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()
Saya menduga ini tidak ada hubungannya dengan python, tetapi itu akan mengkonfirmasinya.
Ya, sepertinya itu berhasil. Penggunaan memori 450MB setelah menjalankannya di IPython, lalu malloc_trim membebaskan 400MB. Sangat merusak
Mengikuti malloc_trim
lead upstream, ini terlihat seperti pengoptimalan glibc yang salah.
xref:
http://sourceware.org/bugzilla/show_bug.cgi?id=14827
lihat komentar "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
Tidak akan memperbaikinya. Mungkin suatu saat kita harus menambahkan beberapa fungsi pembantu ke panda untuk melakukan pemangkasan malloc
Entri di FAQ, mungkin?
Sebagai catatan, kami (+ @ sbneto) menggunakan prediksi ini untuk beberapa waktu, dan melakukannya dengan sangat baik:
# 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 terima kasih banyak!
Tetapi ada operasi lain yang terpengaruh :-(
SANGAT aneh bahwa masalah di atas (masalah glibc) tidak ada reaksi apa pun. Ini mempengaruhi SEMUA lingkungan PC dan server Linux. Dan tidak ada!!!
Saya tahu, Anda akan berkata kepada saya: oke, tulis tambalan! Saya akan melakukannya (UPD: tetapi itu akan menjadi aneh karena saya tidak tahu apa-apa tentang kode glibc). Tapi bahkan tidak ada yang tahu.
Semua orang berkata: KDE bocor. Siapa yang tahu - kenapa ?! Tak seorangpun!
Sumber terbuka? Karena malu! Maaf tapi itu benar untuk situasi ini.
Aku percaya padamu 2 tahun dan tidak ada langkah di sisi itu: /
Saya mengatakan untuk memperbaiki sisi ini dan memberikan komentar menyalahkan yang besar, karena bercabang di sana terlihat tidak layak.
@alanjds Kode Anda memperbaiki masalah bagi saya yang menyebabkan sakit kepala besar. Apakah Anda bersedia menjelaskan apa perilaku panda default dan bagaimana kode Anda memperbaikinya?
Anda juga dapat mengatasi masalah ini dengan beralih ke jemalloc
sebagai pengalokasi default Anda. Alih-alih python script.py
, jalankan LD_PRELOAD=/usr/lib/libjemalloc.so python script.py
. Perhatikan bahwa path ke libjemalloc.so
mungkin berbeda di sistem Anda dan Anda harus menginstalnya terlebih dahulu dengan manajer paket Anda.
@tchristensenowlet Masalahnya tampaknya ada pada kode malloc
dari glibc
. Rupanya, implementasi free
sana tidak menghormati flag yang harus mengeluarkan malloc_trim
setelah ambang batas tertentu, seperti yang Anda lihat di tautan @ghost . Oleh karena itu, malloc_trim
tidak pernah dipanggil dan memori bocor. Apa yang kami lakukan hanyalah memanggil malloc_trim
manual jika lib tersedia di sistem. Kami menyebutnya dalam metode __del__()
, yang dijalankan saat objek dikumpulkan sampah.
glibc.malloc.mxfast
merdu telah diperkenalkan di Glibc (https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Tunables.html).
Saya pikir ini mungkin penyebab salah satu proyek kami, tetapi pengguna kami menjalankan Windows dengan Python 3.8 default (dari situs web resmi) dan dengan semua dependensi diinstal melalui pip. Apakah masalah ini juga ada di Windows? Jika demikian, apa yang setara dengan cdll.LoadLibrary("libc.so.6")
?
Sunting: Saya menjalankan tes yang dijelaskan di sini, dan sampah yang dikumpulkan melakukan tugasnya dengan benar setiap saat:
https://github.com/pandas-dev/pandas/issues/21353
Sistem: Windows 10
Python: 3.8.5
Panda: 1.1.0
Komentar yang paling membantu
Sebagai catatan, kami (+ @ sbneto) menggunakan prediksi ini untuk beberapa waktu, dan melakukannya dengan sangat baik: