collection
, rows
'un aynı oranda küçülmesinden beri, bellek kullanımınız sabit kalacaktır.gc.collect()
numaralı çağrı çok fazla fark yaratmayacak. CPython içinde
Bellek yönetimi ince olduğunu. Sadece referansları kaldırdığınız ve bir toplama döngüsü yürüttüğünüzden, belleğin işletim sistemine geri gönderileceği anlamına gelmez. Bakınız this answer for details.
gerçekten öğelerin büyük listelerinin yerine jeneratörler ve yineleyiciler etrafında bu kodu yapılandırmalıyım, hafızayı kaydetmek için. Bağlantı zaman aşımlarına sahip olduğunuzu söylediğinize çok şaşırdım çünkü tüm satırların getirilmesi bir anda bir satır getirmekten ve yaptığınız basit işlemi gerçekleştirmekten çok daha fazla zaman almamalıdır. Belki de db-getirme koduna bir göz atmalıyız?
satır-at-a-time işleme değil, bir olasılık, o zaman en azından bir iletmenin deque olarak verilerinizi korumak ve jeneratör ve adım adım elde üzerindeki tüm işlem gerçekleştirmek gerçekten ise.
Ben bu farklı yaklaşımları değineceğiz. Her şeyden
Birincisi, bazı ortak işlevler: Bu, mümkün olan en az miktarda alacak
cursor = db.cursor()
cursor.execute("""SELECT * FROM bigtable""")
rowstrings = (tabulate(row) for row in cursor.fetchall())
lines = istrjoin("\n", rowstrings)
file_like_obj.writelines(lines)
cursor.close()
:
# if you don't need random-access to elements in a sequence
# a deque uses less memory and has faster appends and deletes
# from both the front and the back.
from collections import deque
from itertools import izip, repeat, islice, chain
import re
re_redshift_chars = re.compile(r'[abcdefg]')
def istrjoin(sep, seq):
"""Return a generator that acts like sep.join(seq), but lazily
The separator will be yielded separately
"""
return islice(chain.from_iterable(izip(repeat(sep), seq)), 1, None)
def escape_redshift(s):
return re_redshift_chars.sub(r'\\\g<0>', s)
def tabulate(row):
return "\t".join(escape_redshift(str(v)) if v is not None else '' for v in row)
Şimdi idealdir böyle satır-at-a-time işleme vardır bellek - her seferinde sadece bir satır. Eğer gerçekten tüm resultset saklamak gerekiyorsa
, biraz kodunu değiştirebilirsiniz:
cursor = db.cursor()
cursor.execute("SELECT * FROM bigtable")
collection = deque(cursor.fetchall())
cursor.close()
rowstrings = (tabulate(row) for row in collection)
lines = istrjoin("\n", rowstrings)
file_like_obj.writelines(lines)
Şimdi tamamen Tüm programı çalıştırmak için hafızada kalır collection
ilk haline Tüm sonuçları toplamak.
Ancak biz de kullanıldıkları olarak toplama öğeleri silme yaklaşımınızı çoğaltabilirsiniz. kaynak koleksiyonunu çalıştığı için boşaltan bir jeneratör oluşturarak aynı "kod şeklini" koruyabiliriz. Böyle bir şey olacaktır: Kullandığınız kadar belleği boşaltmak istediğinizde
def drain(coll):
"""Return an iterable that deletes items from coll as it yields them.
coll must support `coll.pop(0)` or `del coll[0]`. A deque is recommended!
"""
if hasattr(coll, 'pop'):
def pop(coll):
try:
return coll.pop(0)
except IndexError:
raise StopIteration
else:
def pop(coll):
try:
item = coll[0]
except IndexError:
raise StopIteration
del coll[0]
return item
while True:
yield pop(coll)
Şimdi kolaylıkla
collection
için
drain(collection)
yerine kullanabilirsiniz.
drain(collection)
tükendikten sonra
collection
nesnesi boş olacaktır.
Sinsi olmak istemem ama neden denemiyorsun? –
, birisinin GC'nin genel yüküne ya da gözden kaçan bir python iç nesnesiyle genel değere değip değmeyeceğini biliyor olabilir. – tipu
Mümkünse, tekrarlayıcıları kullanma ve 40k'lık tupları bir kerede işleme sokma, listeyi oluşturma ve aynı anda işleme koyma olasılığını dikkate almaya değer olabilir. Bu, bazı ek karmaşıklık olacak ve ilgili çabaya değmeyebilir. – Moshe