12.03 內存不足、錢包不鼓怎麼辦?三種技巧助你擺脫內存使用困境

選自pythonspeed

作者:Itamar Turner-Trauring

機器之心編譯

參與:高璇、杜偉


內存不足、錢包不鼓怎麼辦?三種技巧助你擺脫內存使用困境

在編寫軟件的過程中,開發人員需要處理大量的數據,因而常常會遇到內存不足的情況。雖然我們都知道,解決內存不足的簡單粗暴方法就是往裡砸錢,但有時自己的經濟實力不允許這麼任性。本文作者提出了三種技巧:數據壓縮、分塊和索引,以此從軟件本身解決了內存不足的窘境。

在你編寫用於處理數據的軟件時,當只用一個小的示例文件做測試,軟件就可以很好地運行。但是,當加載實際數據時,程序立刻崩潰。

原因是沒有足夠的內存——如果你只有 16GB 的 RAM,則無法加載 100GB 的文件。有時操作系統耗盡內存,導致內存無法分配,程序就只能崩潰。

所以,你可以做什麼?要啟動一個大數據集,需要做的是:

  • 獲取計算機集;
  • 花一週的時間進行設置;
  • 在很多情況下,學習全新的 API 並重寫所有代碼。

這樣做簡直讓人心力交瘁;幸運的是,在許多情況下,你也不必這麼做。

你只需一種簡單易用的解決方案:在一臺計算機上用最少的設置即可處理數據,並且儘可能使用你已經在用的庫。而且在很多時候,你可以使用一種被稱為「核外計算」的技術來實現。

在本文中,作者將介紹:

  • 為什麼需要 RAM;
  • 處理內存中不適配數據的最簡單方法:花錢;
  • 處理過多數據的三種基本軟件使用技巧:壓縮、組塊和索引。

文章接下來將展示如何將這些技術應用於 NumPy 和 Pandas 等特定庫。

為什麼需要 RAM?

在繼續討論解決方案之前,讓我們先闡明問題出現的原因。你可以使用計算機的內存(RAM)讀取和寫入數據,但是硬盤驅動器也可以讀取和寫入數據——那麼為什麼計算機需要 RAM 呢?而且磁盤比 RAM 便宜,它通常可以包含所有數據,那麼為什麼代碼不能改為僅從磁盤讀取和寫入數據呢?

從理論上講,這是可行的。但即使是更新、更快的固態硬盤(SSD)也比 RAM 慢得多:

  • 從 SSD 讀取:約 16,000 納秒
  • 從 RAM 讀取:約 100 納秒

如果想要快速計算,數據必須匹配 RAM,否則代碼運行速度可能會慢 150 倍。

解決方案:更多 RAM

解決內存不足的最簡單粗暴的方法就是往裡砸錢。你可以買臺計算機或者租用雲虛擬機(VM),後者的內存比大多數筆記本電腦都要多。以 2019 年 11 月市場價為例,你可以:

  • 購買具有 6 核和 64GB RAM 的 Thinkpad M720 Tower,價格為 1074 美元;
  • 租用具有 64 核和 432GB RAM 的雲虛擬機,每小時 3.62 美元。

這些只是我簡單搜索得到的價格,如果進行一些深入研究,則可能需要更全的價格和產品。

如果花錢就能解決你的內存問題,那通常是最便宜的解決方案

:畢竟時間就是金錢。但是,有時花錢也解決不了問題。

例如,如果你要處理許多數據任務,在一段時間內,雲計算可能是很順手的解決方案,但卻也是昂貴的解決方案。在一項研究工作中,我所使用軟件的計算成本將耗盡該產品的所有預計收入,包括我的薪水在內,這樣代價就太大了。

如果購買/租用更多的 RAM 是不夠或不現實的,下一步就是弄清楚如何通過更改軟件來減少內存使用。

技巧 I:數據壓縮

數據壓縮意味著使用更少的內存來表示數據。壓縮有兩種形式:

  • 無損:存儲的數據與原始數據信息完全相同;
  • 有損:存儲的數據丟失了原始數據中的某些細節,但在理想情況下不會對計算結果產生太大影響。

請注意,我說的不是 ZIP 或 gzip 文件,因為這些文件通常涉及磁盤壓縮。要處理 ZIP 文件中的數據,首先需要解壓縮到 RAM 中。因此,這無濟於事。

你需要的是壓縮內存中的表示形式。

例如,假設你的數據有兩個值,並且將永遠只有這兩個值:"AVAILABLE"和"UNAVAILABLE"。你可以將它們存儲為布爾值,True 或 False,這樣可以將其存儲為 1 個字節,而不是每個條目都要佔用 10 個甚至更多字節。你甚至可以將表示降低到表示布爾值所需的單個位,從而將內存使用量減少到原來的八分之一。

技巧 II:分塊,一次加載一個數據塊

當你需要處理所有數據但不需要一次將所有數據加載到內存中時,分塊很有用。你可以將數據分塊加載到內存中,一次只處理一個數據塊(或者按照後文提到的,並行處理多個塊)。

例如,假設你要查找一本書中最長的單詞。你可以一次將所有數據加載到內存中:

largest_word = ""
for word in book.get_text().split():
if len(word) > len(largest_word):
largest_word = word

即使假設在我們的情況下,書不適配內存,可以將其改為一頁一頁的加載。

largest_word = ""
for page in book.iterpages():
for word in page.get_text().split():
if len(word) > len(largest_word):
largest_word = word

需要使用的內存要少得多,因為在任何給定的時間內只有一頁書在內存中只有一頁書在內存中。最後,你還是會得到相同的答案。


技巧 III:在你需要數據子集時進行索引

當你只需要使用數據的一個子集,並且希望在不同的時間加載數據的不同子集時,索引很有用。

你可以通過分塊解決這種情況:每次加載所有數據,然後過濾掉不需要的數據。但這很慢,因為需要加載許多不相關的數據。

如果只需要部分數據,則最好使用索引,而不是分塊,索引最好使用數據摘要,它可以告訴你在哪裡找到所需的數據。

想象一下,您只想閱讀本書中有關土豚(ardarvarks)的部分。如果使用分塊,則需要逐頁閱讀整本書,以查找 ardarvarks,但這將花費相當長的時間。

或者,你可以利用書的索引,找到「Aardvarks」的條目。它可能會告訴你閱讀第 7、19 和 120-123 頁。現在你就可以閱讀這些頁面,並且僅閱讀這些頁面,這要快得多。

這樣之所以可行,是因為索引比整本書要小得多,因此將索引加載到內存中以查找相關數據要容易得多。

最簡單的索引技術

實現索引的最簡單、最常見方法是在目錄中命名文件:

mydata/
2019-Jan.csv
2019-Feb.csv
2019-Mar.csv
2019-Apr.csv
...

如果要獲取 2019 年 3 月的數據,則只需加載 2019-Mar.csv 即可,而無需加載 2 月、7 月或任何其他月份的數據。

下一步:應用這些技術

解決 RAM 不足的最簡單方法是花錢獲得更多 RAM。但是,如果這行不通或者不現實時,就可以使用壓縮、分塊或索引等技巧。

這些技巧可以應用在許多不同的軟件包和工具中。即使大數據系統也基於這些技巧構建:例如,使用多臺計算機來處理數據塊。

原文鏈接:https://pythonspeed.com/articles/data-doesnt-fit-in-memory/


分享到:


相關文章: