用記憶體管理講解為何 python 的 list 那麼慢

張憲騰
5 min readApr 12, 2019

Python 雖然好用,但也經常被別人詬病他的執行速度非常慢,

但為什麼慢?我們可以用什麼方式解決?

這邊想跟大家分享一些 記憶體的觀念,讓大家知道用 Python 也是可以用得很聰明得。

這邊你會看到

  1. 程式是如何跟記憶體進行合作的
  2. numpy.array 跟 list 的差別,為什麼我們都會建議使用 array 進行運算

先說說一些記憶體的小常識吧

當你在 python 內執行 a=5 的時候,你知道電腦做了什麼事嗎?

python 會去跟電腦要一塊記憶體,把 5 存在這塊記憶體內,並且把記憶體位置記下(如 0x001 ),這時他們會把 a 和另一個記憶體位置記在一個 Mapping Table 上,如下圖:

這時當你之後再呼叫 a 這個變數的時候,程式就會知道要到0x001去找裡面儲存的值(回應 5)

這邊講完了記憶體的小概念,下面就要更接近主題了。

在進行資料處理時,我們極力推薦 numpy.array 而不是 python 原生的 list

大家都說 Python 在處理資料的速度慢很多,當然就有大神發現在極度方便的 Python 需要一個套件可以加快他的速度,此時 numpy 就出來的,裡面有一個非常重要的觀念就是 numpy.array

array 跟 list 差在哪裡呢?為什麼 array 會跑得比 list 還要快呢?

這邊我們先來看張圖讓大家可以有個全面的概念:

圖片來源:Why Python is slow

從上圖可以看到,Python list 內部是儲存一堆指標( pointer ),而 array 是直接把 data 存在裡面,這雙方的差別又在哪裡呢?就先讓我們分開說明吧!

list 的運作原理

這邊先講 list,python 的 list 真的很好用,他可以用 append自動把元素加入list 裡面,而在一個 list 裡面也可以儲存不同型態的資料,例如:

a_list = [3, [‘Sam’, ‘Hands’] ,( [‘Brian’],42,’Python’)],我們如果要加入資料只要 a_list.append(33) 即可

但他在記憶體內是怎麼實作的呢?

首先記憶體內會要 a_list 數量的記憶體空間,去把 a_list[0], a_list[1], … 的資料存進去

接著程式再去要一塊空間生成一個空的 list 並存入這些指向元素的 pointer ,產生一個記憶體位置,EX: a_list = 0x002 ,並記錄在 Mapping Table 內。

此時你的 a_list[0] 可能存入的是 0x003 , 然後他指向的記憶體位置內 3 這個值,所以當我們寫出 print(a_list[0])時,他會先指向 a_list 的位置,把那個存入很多 pointer 的 list 拿出來,之後再找到第 0 個存入的位置,再去那塊記憶體位置把 3 拿出來,印在你的電腦上。

透過以上的解釋,這也就是為什麼我們的 list 內可以儲存許多不同型態的值,而且也都可以自由地新增與刪除了。

array 的運作原理

那 array 呢? array 是過往底層語言會用的記憶體操作方法,他的效益就是比較快也比較省空間,為什麼呢?有兩個原因:

  1. 一開始陣列內部的型態定義就定義好了,所以不會需要每次都到底層的 vtable去做比對;
  2. 要記憶體方式不同:不同於 list,array在要記憶體是直接要一大塊,並且把資料直接儲存在這個空間內,這樣便少了一個 list 和 pointer 的儲存空間,也就比較省空間,且要到資料的速度也比較快了。

第一點,我想很直接,第二點這邊我們在做更多的解釋

當我們今天宣告 a_array = np.array([[1,2,3],[4,5,6]]),電腦做了什麼事呢?

程式會先跟電腦要一大塊記憶體,必須是連續且不能斷裂的,這時這他就將這個大 array 就丟進這個大塊記憶體內,並且各自找到一個空間,去記錄下來。

之後便在 Mapping Table 內去紀錄 a_array 在哪個記憶體位置,因為上面做過很多次了,這邊就不在重複了。

這樣為什麼比較快呢?

想一想你今天要去找你同學,你只有了一個地址,Array 給你的地址就是你同學家的地址,List 給你地址是鄉長家,你還要問他:「你知不知道 Sam 住哪啊?」

「他住在三條街右轉的第二間。」

這樣哪個比較快呢?更不用說假如現在是巢狀的結構,那肯定又更慢了。

但 numpy.array 也有一些缺點,例如 numpy.array 不能夠自由增減元素,裡面也不能存取不同型態的資料(否則整個資料型態會以儲存最大記憶體的型態存取),在操作上會很不方便。

但我用 python 就是要方便啊!有沒有折衷的方案,有的,這時你就不得不讚嘆前人的技術,他們開發出一個神之套件 — pandas

這邊留個伏筆,若是 claps 的數量高於 300,代表文章有人想看嗚嗚

這樣下次會跟大家說 pandas 是如何運用內部套件 blockManager 結合 numpy.array 跟方便性的。

ps. 這邊因為是一個科普文章,所以在記憶體內很多的觀念都是打到為止,例如說:a = 5 他其實是記住儲存 5 的記憶體位置的第一位(一個int8 佔據 1 byte) ,希望高手看到不要太苛責。

如何降低 Pandas 80% 效率的秘訣也寫完啦!歡迎各位收看~

謝謝大家的觀看,如果覺得這篇文章對您有幫助,也不忘幫我拍手喔。請幫忙按 5 次 LikeButton 化讚為賞,回饋創作;如果登記 LikeCoin ID,登入後再按 LikeButton 我會得到更多獎賞,非常感謝!

--

--