Python'da hafızayı nasıl boşaltabilirim?


387

Üçgenleri temsil eden birkaç milyon nesne oluşturmak için büyük bir girdi dosyasına etki eden bir Python programı yazdım. Algoritma:

  1. bir girdi dosyasını oku
  2. dosyayı işleyin ve köşeleriyle gösterilen üçgenlerin bir listesini oluşturun
  3. köşeleri OFF biçiminde çıktılar: köşelerin listesi ve ardından üçgen listesi. Üçgenler, dizinler listesinde endekslerle temsil edilir

Üçgenleri yazdırmadan önce köşelerin tam listesini yazdırmamın KAPALI olması, çıktıyı dosyaya yazmadan önce bellekte üçgen listesini tutmam gerektiği anlamına gelir. Bu arada listelerin boyutları nedeniyle bellek hataları alıyorum.

Python'a artık bazı verilere ihtiyacım olmadığını ve serbest bırakılabileceğini söylemenin en iyi yolu nedir?


11
Üçgenleri neden bir ara dosyaya yazdırıp ihtiyaç duyduğunuzda tekrar okuyorsunuz?
Alice Purcell

2
Bu soru potansiyel olarak iki farklı şey hakkında olabilir. Bu Python işleminden kaynaklanan bu hatalar mı , bu durumda Python işleminin yığınına bellek boşaltmayı önemsiyoruz veya sistemdeki farklı işlemlerden mi oluşuyor, bu durumda işletim sistemine bellek boşaltmayı önemsiyoruz?
Charles Duffy

Yanıtlar:


454

Python Resmi Belgelerine göre , Çöp Toplayıcı'yı referanssız belleği serbest bırakmaya zorlayabilirsiniz gc.collect(). Misal:

import gc
gc.collect()

19
Bazı olağandışı durumlar dışında işler sık ​​sık toplanan çöplerdir, bu yüzden bunun çok yardımcı olacağını düşünmüyorum.
Lennart Regebro

24
Genel olarak gc.collect () yönteminden kaçınılmalıdır. Çöp toplayıcı işini nasıl yapacağını bilir. Bununla birlikte, OP birdenbire çok sayıda nesneyi (milyonlarda olduğu gibi) dağıttığı bir durumda ise , gc.collect yararlı olabilir.
Jason Baker

165
Aslında gc.collect()kendinizi bir döngünün sonunda çağırmak , belleğin parçalanmasından kaçınmaya yardımcı olabilir ve bu da performansı artırmaya yardımcı olur. Bunun önemli bir fark
yarattığını

39
Python 3.6 kullanıyorum. gc.collect()Bir panda veri çerçevesini hdf5'ten (500 bin satır) yükledikten sonra arama, bellek kullanımını 1,7 GB'tan 500 MB'a düşürdü
John

15
32GB belleğe sahip bir sistemde 25GB'lık birkaç numerik dizi yüklemem ve işlemem gerekiyor. Diziyi işledikten sonra del my_arraybunu kullanmak gc.collect()belleği gerçekten serbest bırakmanın tek yoludur ve işlemim bir sonraki diziyi yüklemek için hayatta kalır.
David

113

Maalesef (sürümünüze ve Python sürümünüze bağlı olarak) bazı nesne türleri, düzenli bir yerel optimizasyon olan ancak özellikle belirli bir tür nesneler için daha fazla bellek "ayrılmış" yaparak bellek parçalanmasına neden olabilecek "ücretsiz listeler" kullanır. dolayısıyla "genel fon" tarafından kullanılamaz.

Büyük ama geçici bir bellek kullanımının YAPILDIĞINDA tüm kaynakları sisteme geri döndürmesini sağlamanın tek güvenilir yolu, bu kullanımın belleğe aç çalışmaların sona erdiği bir alt işlemde gerçekleşmesini sağlamaktır. Bu koşullar altında, işletim sistemi işini YAPACAK ve alt sürecin kattığı tüm kaynakları memnuniyetle geri dönüştürecektir. Neyse ki, multiprocessingmodül Python'un modern versiyonlarında bu tür bir işlemi (eskiden bir acıydı) çok kötü değil.

Kullanım durumunuzda, alt işlemlerin bazı sonuçlar biriktirmesinin ve bu sonuçların ana işlem için kullanılabilir olmasını sağlamanın en iyi yolunun yarı geçici dosyaları kullanmak olduğu anlaşılmaktadır (yarı geçici olarak, yani tür dosyaları değil kapatıldığında otomatik olarak kaybolur, yalnızca işiniz bittiğinde açıkça sildiğiniz sıradan dosyalar).


31
Bunun önemsiz bir örneğini görmek istiyorum.
Aaron Hall

3
Ciddi anlamda. @AaronHall ne dedi.
Noob Saibot

17
@AaronHall Paylaşılan durumu uygulamak için dosyaları kullanmak yerine artık önemsiz bir örnek mevcutmultiprocessing.Manager .
user4815162342

48

delDeyimi kullanım olabilir, ama IIRC belleği boşaltmaya garanti edilmez . Docs burada ... ve bir o serbest bırakılmaz neden burada .

Linux ve Unix tipi sistemlerde insanların bir python işleminin biraz iş yapmasını, sonuç almasını ve sonra öldürmesini istediğini duydum.

Bu makalede Python çöp toplayıcı hakkında notlar var, ancak bellek kontrolü eksikliği yönetilen bellek için olumsuz olduğunu düşünüyorum


IronPython ve Jython bu sorunu önlemek için başka bir seçenek olabilir mi?
Esteban Küber

@voyager: Hayır, olmazdı. Ve başka hiçbir dil de olmazdı. Sorun, bir listeye büyük miktarda veri okuması ve verilerin bellek için çok büyük olmasıdır.
Lennart Regebro

1
IronPython veya Jython altında muhtemelen daha kötü olurdu . Bu ortamlarda, başka hiçbir şey referans tutmadığında belleğin serbest bırakılacağı garanti edilemez.
Jason Baker

@voyager, evet, çünkü Java sanal makinesi hafızanın boş olması için global olarak arama yapıyor. JVM için, Jython özel bir şey değildir. Öte yandan, JVM'nin kendi dezavantajları payı vardır, örneğin, ne kadar büyük yığın kullanabileceğini önceden bildirmeniz gerekir.
Prof. Falken sözleşmesi

32

Python çöp toplanır, bu nedenle listenizin boyutunu küçültürseniz belleği geri kazanır. Bir değişkenten tamamen kurtulmak için "del" ifadesini de kullanabilirsiniz:

biglist = [blah,blah,blah]
#...
del biglist

18
Bu doğru ve doğru değil. Listenin boyutunu küçültmek belleğin geri kazanılmasına izin verirken, bunun ne zaman olacağının garantisi yoktur.
user142350

3
Hayır, ama genellikle yardımcı olur. Ancak, burada soruyu anladığım kadarıyla, sorun o kadar çok nesneye sahip olması gerektiğidir, eğer hepsini bir listeye okursa, hepsini işlemeden önce hafızası tükenir. İşlem tamamlanmadan listeyi silmek yararlı bir çözüm değildir. ;)
Lennart Regebro

3
Yetersiz bellek / yetersiz bellek durumu, çöp toplayıcının "acil durum çalışmasını" tetiklemez mi?
Jeremy Friesner

4
biglist = [] belleği serbest bırakacak mı?
neouyghur

3
evet, eski listeye başka bir şey referans verilmiyorsa.
Ned Batchelder

22

Belleği açıkça boşaltamazsınız. Yapmanız gereken şey, nesnelere referans tutmamanızdır. Daha sonra çöp toplanacak ve hafızayı boşaltacaktır.

Sizin durumunuzda, büyük listelere ihtiyacınız olduğunda, genellikle kodu jeneratörler / yineleyiciler kullanarak yeniden düzenlemeniz gerekir. Bu şekilde büyük listelerin hafızada olmasına gerek kalmaz.

http://www.prasannatech.net/2009/07/introduction-python-generators.html


1
Bu yaklaşım uygunsa, muhtemelen yapmaya değer. Ancak yineleyicilere rastgele erişim yapamayacağınız ve bu da sorunlara neden olabileceği belirtilmelidir.
Jason Baker

Bu doğrudur ve gerekliyse, büyük veri veri kümelerine rasgele erişmenin bir tür veritabanı gerektirmesi muhtemeldir.
Lennart Regebro

Başka bir yineleyicinin rasgele bir alt kümesini çıkarmak için bir yineleyiciyi kolayca kullanabilirsiniz.
S.Lott

Doğru, ama sonra alt seti almak için her şeyi tekrarlamanız gerekir, ki bu çok yavaş olacaktır.
Lennart Regebro

21

( delbaşka referans olmadığında nesneleri silinebilir olarak işaret ettiği için arkadaşınız olabilir. Şimdi, CPython yorumlayıcısı bu belleği daha sonra kullanmak üzere saklar, bu nedenle işletim sisteminiz "serbest" belleği görmeyebilir.)

Belki de verileriniz için daha kompakt bir yapı kullanarak ilk etapta herhangi bir hafıza problemi yaşamazsınız. Bu nedenle, numara listeleri standart arraymodül veya üçüncü taraf numpymodül tarafından kullanılan formattan çok daha az bellek verimlidir . Köşelerinizi bir NumPy 3xN dizisine ve üçgenlerinizi bir N öğesi dizisine koyarak bellekte tasarruf edersiniz.


Eh? CPython'un çöp koleksiyonu yeniden sayım tabanlıdır; periyodik bir işaretleme ve süpürme değildir (birçok yaygın JVM uygulaması için olduğu gibi), ancak bunun yerine referans sayısının sıfıra düştüğü anda bir şeyi hemen siler. Yalnızca döngüler (yeniden sayımların sıfır olacağı, ancak referans ağacındaki döngülerden kaynaklanmadığı durumlarda) periyodik bakım gerektirir. delbir nesneyi referans alan tüm isimlere farklı bir değer atayan hiçbir şey yapmaz.
Charles Duffy

Nereden geldiğini görüyorum: Cevabı buna göre güncelleyeceğim. CPython yorumlayıcısının aslında ara bir yolla çalıştığını anlıyorum: delbelleği Python'un bakış açısından serbest bırakır, ancak genellikle C çalışma zamanı kitaplığının veya OS'nin bakış açısından kurtarmaz. Kaynaklar: stackoverflow.com/a/32167625/4297 , effbot.org/pyfaq/… .
Eric O Lebigot

Bağlantılarınızın içeriğiyle ilgili olarak kabul edildi, ancak OP'nin aynı Python işleminden aldıkları bir hatadan bahsettiği varsayılarak, belleği yerel işlem yığınına ve işletim sistemine boşaltmak arasındaki fark önemli görünmüyor ( öbeğe serbest bırakılması, o alanı söz konusu Python işlemi içindeki yeni tahsisler için kullanılabilir hale getirir). Ve bunun için, delkapsam dışı çıkışlar, yeniden
Charles Duffy

11

Bir dosyadan grafik okuma benzer bir sorun vardı. İşleme, belleğe sığmayan 200 000x200 000 şamandıralı bir matrisin (her seferinde bir satır) hesaplanmasını içeriyordu. gc.collect()Sorunun belleğe bağlı yönünü kullanarak hesaplamalar arasında belleği boşaltmaya çalışmak, ancak performans sorunlarına neden oldu: Neden bilmiyorum ama kullanılan bellek miktarı sabit kalsa da, her yeni çağrı gc.collect()daha fazla zaman aldı bir önceki. Çok hızlı bir şekilde çöp toplama işlemi hesaplama süresinin çoğunu aldı.

Hem bellek hem de performans sorunlarını gidermek için bir kerede bir yerde okuduğum çok iş parçacıklı bir hile kullanımına geçtim (Üzgünüm, artık ilgili yazıyı bulamıyorum). Büyük bir fordöngü içinde dosyanın her satırını okumadan önce , onu işliyor ve gc.collect()bellek alanını boşaltmak için her seferinde çalışıyor . Şimdi yeni bir iş parçacığında dosyanın bir yığınını okuyan ve işleyen bir işlev çağırıyorum. İş parçacığı sona erdiğinde, garip performans sorunu olmadan bellek otomatik olarak serbest bırakılır.

Pratik olarak şu şekilde çalışır:

from dask import delayed  # this module wraps the multithreading
def f(storage, index, chunk_size):  # the processing function
    # read the chunk of size chunk_size starting at index in the file
    # process it using data in storage if needed
    # append data needed for further computations  to storage 
    return storage

partial_result = delayed([])  # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100  # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
    # we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
    partial_result = delayed(f)(partial_result, index, chunk_size)

    # no computations are done yet !
    # dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
    # passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
    # it also allows you to use the results of the processing of the previous chunks in the file if needed

# this launches all the computations
result = partial_result.compute()

# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided

1
Yorumlar için Python'da # yerine neden // // `` kullandığınızı merak ediyorum.
JC Rocamonde

Diller arasında karıştım. Yorum için teşekkürler, sözdizimini güncelledim.
Retzod

9

Diğerleri, Python yorumlayıcısını belleği boşaltmak için "koaksiyel" edebileceğiniz (veya bellek sorunlarından kaçınabileceğiniz) bazı yollar yayınladı. Muhtemelen ilk önce fikirlerini denemelisiniz. Ancak, sorunuza doğrudan cevap vermenin önemli olduğunu düşünüyorum.

Doğrudan Python'a hafızayı boşaltmasını söylemenin hiçbir yolu yoktur. Gerçek şu ki, bu kadar düşük bir kontrol seviyesi istiyorsanız, C veya C ++ 'da bir uzantı yazmak zorunda kalacaksınız.

Bununla birlikte, bu konuda yardımcı olacak bazı araçlar var:


3
Büyük miktarda bellek kullanırken gc.collect () ve del gc.garbage [:] gayet iyi çalışıyor
Andrew Scott Evans

3

Köşe yeniden kullanımını umursamıyorsanız, biri köşe noktaları diğeri üçgenler için olmak üzere iki çıktı dosyanız olabilir. Ardından, işiniz bittiğinde üçgen dosyasını köşe dosyasına ekleyin.


1
Sadece hafızadaki köşeleri tutabildiğimi ve üçgenleri bir dosyaya yazdırabileceğimi ve sonra yalnızca sonunda köşeleri yazdırabileceğimi düşünüyorum. Ancak, üçgenleri bir dosyaya yazma eylemi büyük bir performans drenajıdır. Bunu hızlandırmanın bir yolu var mı ?
Nathan Fellman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.