Pandas: Masalah GC siklik

Dibuat pada 8 Jan 2013  ·  14Komentar  ·  Sumber: pandas-dev/pandas

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()
Bug

Komentar yang paling membantu

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)

Semua 14 komentar

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.

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

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

Apakah halaman ini membantu?
1 / 5 - 1 peringkat