前言
本來程式跑得好好的,突然快到結尾時程式被強制關閉,報錯 zsh: killed xxx/bin/python

後面經查找發現是因為記憶體耗盡,程式強制被關閉,追其溯源,發現使用了類似這樣的代碼:
從一個檔案夾下讀取全部檔案資料作為list傳回,由于list太大導緻記憶體不足
能不能通過分塊或者惰性加載的思想來疊代nc_datas進而解決記憶體瓶頸問題?
答案是可以的
協程模型
python中的協程采用了generator實作,而generator就類似惰性加載的思想,你調用它一次它才計算一次。比如 x * x 就是generator,它代表了一個理論上無限長的 list 集合,但實際上占用的記憶體隻有目前的值,這也與Java中的Stream是類似的
由于前面記憶體耗盡、程式強制被關閉時,讀資料占了大概10G的記憶體,是以我想分3~4次讀取,這樣每次隻會消耗2~3G的記憶體,不會出現記憶體瓶頸問題
下面給出一個協程簡化模型
# 判斷目前下标c在第幾個段
def check(c,red_lines):
for i,red_v in enumerate(red_lines):
if c < red_v:
return i
return len(red_lines)
# 比如3個段就會cut出2條分界線
def cut(x,chunck_count):
per_num = len(x) / chunck_count
red_lines = []
# 比如分3段處理就有3-1=2條線(切2刀)
for i in range(chunck_count - 1):
red_lines.append((i + 1) * per_num)
return red_lines
# 讀資料
def read_nc_files():
# 假設一共有10個檔案要讀取
filepaths = ["1.nc","2.nc","3.nc","4.nc","5.nc","6.nc","7.nc","8.nc","9.nc","10.nc"]
# 切成3次讀取
red_lines = cut(filepaths,3)
ans = []
old = 0
for i,v in enumerate(filepaths):
idx = check(i, red_lines)
if idx != old:
old = idx
# 協程傳回,下一步轉到代碼 "for data in datas:" 處進行下一次周遊
yield ans
del ans
ans = []
ans.append(v)
yield ans
def test():
# 由于python檢測到read_nc_files函數裡有yield關鍵字
# 是以将其作為生成器generator傳回,即這裡datas不是list,而是一個generator
datas = read_nc_files()
# 對于周遊generator,周遊一次計算一次(傳回一次)
# 而不是一次性把所有datas傳回
# 周遊後的下一步将跳轉到代碼 "yield ans" 處并繼續往下執行
for data in datas:
print(data)
test()
實驗将10個檔案切成3次讀取,列印結果:
對于11、12、13個檔案測試,同樣列印結果:
可以看到采用協程的記憶體瓶頸解決方案成功