從cpu和記憶體來理解為什麼陣列比鍊錶查詢快

2021-08-14 13:49:16 字數 1546 閱讀 1454

1.arraylist和linkedlist可想從名字分析,它們乙個是array(動態陣列)的資料結構,乙個link(鍊錶)的資料結構,此外,它們兩個都是對list介面的實現。前者是陣列佇列,相當於動態陣列;後者為雙向鍊錶結構,也可當作堆疊、佇列、雙端佇列

2.arraylist查詢效率比linklist高,因為查詢時候linklist需要移動指標乙個個查詢

3.linklist新增和刪除效率比arraylist高,linklist底層為雙向鍊錶結構,會記錄前乙個節點和後乙個節點,所以新增時候與刪除時候更快。

乙個常見的程式設計問題: 遍歷同樣大小的陣列和鍊錶, 哪個比較快? 如果按照大學教科書上的演算法分析方法,你會得出結論,這2者一樣快, 因為時間複雜度都是 o(n)。 但是在實踐中, 這2者卻有極大的差異。 通過下面的分析你會發現, 其實陣列比煉表要快很多。

首先介紹乙個概念:memory hierarchy (儲存層次結構),電腦中存在多種不同的儲存器,如下表

cpu 暫存器 – immediate access (0-1個cpu時鐘週期)

cpu l1 快取 – fast access (3個cpu時鐘週期)

cpu l2 快取 – slightly slower access (10個cpu時鐘週期)

記憶體 (ram) – slow access (100個cpu時鐘週期)

硬碟 (file system) – very slow (10,000,000個cpu時鐘週期)

(資料來自

各級別的儲存器速度差異非常大,cpu暫存器速度是記憶體速度的100倍! 這就是為什麼cpu產商發明了cpu快取。 而這個cpu快取,就是陣列和鍊錶的區別的關鍵所在。

cpu快取會把一片連續的記憶體空間讀入, 因為陣列結構是連續的記憶體位址,所以陣列全部或者部分元素被連續存在cpu快取裡面, 平均讀取每個元素的時間只要3個cpu時鐘週期。 而鍊錶的節點是分散在堆空間裡面的,這時候cpu快取幫不上忙,只能是去讀取記憶體,平均讀取時間需要100個cpu時鐘週期。 這樣算下來,陣列訪問的速度比煉表快33倍! (這裡只是介紹概念,具體的數字因cpu而異)

因此,程式中盡量使用連續的資料結構,這樣可以充分發揮cpu快取的威力。 這種對快取友好的演算法稱為 cache-oblivious algorithm, 有興趣可以參考相關資料。再舉乙個簡單例子:

對比

for i in

0..n

for j in

0..m

for k in

0..p

c[j] = c[j] + a[k] * b[k][j];

和for i in

0..n

for k in

0..p

for j in

0..m

c[j] = c[j] + a[k] * b[k][j];

雖然兩者執行結果一樣,演算法複雜度也一樣,但是你會發現第二種寫法要快很多。

總結一下, 各種儲存器的速度差異很大,在程式設計中絕對有必要考慮這個因素。 比如,記憶體速度比硬碟快1萬倍,所以程式中應該盡量避免頻繁的硬碟讀寫;cpu快取比記憶體快幾十倍,在程式中盡量多加利用。

從渲染過程來理解opengl座標系和座標變換投影

世界座標系,以人站立的位置為原點,右方為x 正方形,頭頂為y的正方形,z的正方形為腦後 即人看向z的負方向 如下圖 因此,在用opengl繪製的影象時,載入的模型檔案其實都是在原點的位置,在繪製遊戲或者模型的時候就要將這些模型放置在不同的世界座標系位置上,即稱為絕對座標。絕對座標就是我們要繪製的物體...

從物件的記憶體角度來理解「父類的引用指向子類的物件」

從物件的記憶體角度來理解試試 假設現在有乙個父類 father,它裡面的變數需要占用 1m記憶體 有乙個它的子類 son,它裡面的變數需要占用 0.5m記憶體.現在通過 來看看記憶體的分配情況 father f new father 系統將分配 1m記憶體 son s new son 系統將分配 1...

從物件的記憶體角度來理解「父類的引用指向子類的物件」

多型中總不理解father f new son 是怎麼引用子類物件的,現在從從物件的記憶體角度來理解試試 假設現在有乙個父類 father,它裡面的變數需要占用 1m記憶體 有乙個它的子類 son,它裡面的變數需要占用 0.5m記憶體.現在通過 來看看記憶體的分配情況 father f new fa...