所以我試圖將帶有逗號分隔值的大量字串決議為(一個串列)串列。我正在嘗試“就地”執行此操作,例如不復制記憶體中已經很大的物件。
現在,理想情況下,在決議期間和之后,唯一需要的額外記憶體是將原始字串表示為字串串列的開銷。但實際發生的事情要糟糕得多。
例如,這個字串串列占用了大約 1.36 GB 的記憶體:
import psutil
l = [f"[23873498uh3149ubn34, 59ubn23459un3459, un3459-un345, 9u3n45iu9n345, {i}]" for i in range(10_000_000)]
psutil.Process().memory_info().rss / 1024**3
>> 1.3626747131347656
所需的決議最終結果會占用更多(~1.8 GB):
import psutil
l = [["23873498uh3149ubn34", "59ubn23459un3459", "un3459-un345", "9u3n45iu9n345", str(i)] for i in range(10_000_000)]
psutil.Process().memory_info().rss / 1024**3
1.7964096069335938
然而,實際上對原始字串進行決議需要高達 5 GB 的記憶體,即比初始字串加上最終串列的記憶體還要多:
import psutil
l = [f"[23873498uh3149ubn34, 59ubn23459un3459, un3459-un345, 9u3n45iu9n345, {i}]" for i in range(10_000_000)]
for i, val in enumerate(l):
l[i] = val.split(", ")
psutil.Process().memory_info().rss / 1024**3
4.988628387451172
Now, I understand that pure Python strings and lists themselves are not terribly efficient (memory-wise), but I fail to understand that huge, huge gap between the memory required for the final result (1.8GB), and what's used in the process to get there (5GB).
Can anyone explain what exactly is going on, whether it is possible to actually modify lists "in place" (while actually freeing the memory of replaced values), and whether there is a better in-memory way to do this?
uj5u.com熱心網友回復:
重新格式化使其可讀,您對它“應該占用”的記憶體的估計非常樂觀:
l = [["23873498uh3149ubn34",
"59ubn23459un3459",
"un3459-un345",
"9u3n45iu9n345",
str(i)] for i in range(10_000_000)]
那就太離譜了。例如,只為 1 創建了一個字串物件"9u3n45iu9n345",它在 1000 萬個串列中共享。
>>> print(l[10][2] is l[56000][2]) # first indices are arbitrary
True
您的實際代碼在每個索引位置創建 1000 萬個不同的字串物件。
嘗試運行
sys._debugmallocstats()
不時地獲得有關記憶體用于什么用途的更多線索。
在 3.10.1 下,在 64 位 Windows 上,最后的一些摘要輸出顯示分配的 RAM 正在被非常有效地使用(幾乎所有分配的塊都在使用中):
# arenas allocated total = 6,601
# arenas reclaimed = 1,688
# arenas highwater mark = 4,913
# arenas allocated current = 4,913
4913 arenas * 1048576 bytes/arena = 5,151,653,888
# bytes in allocated blocks = 5,129,947,088
# bytes in available blocks = 701,584
53 unused pools * 16384 bytes = 868,352
# bytes lost to pool headers = 15,090,192
# bytes lost to quantization = 5,046,672
# bytes lost to arena alignment = 0
Total = 5,151,653,888
arena map counts
# arena map mid nodes = 1
# arena map bot nodes = 20
# bytes lost to arena map root = 8,192
# bytes lost to arena map mid = 8,192
# bytes lost to arena map bot = 40,960
Total = 57,344
注意:節省大約 8 億位元組的簡單方法:
l[i] = tuple(val.split(", ")) # use this
# l[i] = val.split(", ") # instead of this
O(1)列出“過度分配”,以便在攤銷時間內運行一系列附加。元組不會——它們只分配所需的數量來保存它們的初始內容(這也是它們的最終內容,因為元組是不可變的)。
uj5u.com熱心網友回復:
如果您不一次使用整個串列,則可以使用生成器運算式
import psutil
data = (f"[23873498uh3149ubn34, 59ubn23459un3459, un3459-un345, 9u3n45iu9n345, {i}]" for i in range(10_000_000))
res = (x.split(', ') for x in data)
print(psutil.Process().memory_info().rss / 1024**3) # 0.08874893188476562
uj5u.com熱心網友回復:
測量 RSS 是錯誤的。RSS 是當前駐留在物理 RAM 中的虛擬記憶體的一部分。例如,如果我只有 4 GB 的物理 RAM,我將永遠不會測量超過 4 GB。所以:如果你想保持這個數字很小,請從計算機中移除 RAM 模塊。
字串中的資料約為 650 MB。
一個串列需要 56 個位元組的記憶體。您正在創建 10.000.000 個,因此這些串列占了 560 MB 的額外記憶體。
一個字串在記憶體中占用 49 個位元組,加上字符本身。將長字串分成幾段后,您有 50.000.000 個字串,因此它們又占 2450 MB。
另一個問題是垃圾收集。使用 PSUtils,您可以向作業系統詢問它為 Python 提供了多少記憶體。但是,Python 可能只是請求了它,因為它認識到您正在處理大量資料。但是,其中一些仍然可以被認為在Python內部是免費的。
除此之外,我聲稱您應該進行真實世界的測驗。一個好的 CSV 決議器不會將整個檔案加載到記憶體中然后拆分字串。相反,它將有一個決議器,可以流式傳輸資料并在找到時以逗號分隔。
為了獲得課程的開銷,我使用了
import sys
print(sys.getsizeof([]))
print(sys.getsizeof(""))
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/441603.html
標籤:python list memory-management
下一篇:如何將其轉換為串列理解
