數(shù)據(jù)結構是如何裝入CPU寄存器的?
大家好,我是小風哥。
我們在之前很多文章的講解中涉及了CPU與寄存器,然后有同學問了這樣一個問題:既然CPU內部的寄存器數(shù)量有限,容量有限,那么我們使用的龐大的數(shù)據(jù)結構是怎樣裝入寄存器供CPU計算的呢?
這篇文章就為你講解一下這個問題。
內存與數(shù)據(jù)
真正有用的程序是離不開數(shù)據(jù)的,比如一個int、一個float等,這些都是非常簡單的數(shù)據(jù)。
當然也有非常復雜的數(shù)據(jù),這樣的數(shù)據(jù)通常在內存中以數(shù)據(jù)結構的形式組織起來,比如你創(chuàng)建了一個數(shù)組、一個鏈表、創(chuàng)建了一棵樹、一張圖,就像這樣:
那么很顯然這些數(shù)據(jù)存放在內存中,而且這些數(shù)據(jù)在不同的場景下有不同的大小,從數(shù)B、數(shù)KB到數(shù)百GB都有可能,與此同時,CPU內部的寄存器數(shù)量是固定的,容量也是極其有限的,那么CPU是如何利用有限的資源操作龐大的數(shù)據(jù)結構呢?
要回答這一問題,我們需要要認識一位農夫,因為他不生產(chǎn)數(shù)據(jù),他只是數(shù)據(jù)的搬運工,這位農夫就是。。
搬運數(shù)據(jù)的機器指令
你沒有看錯,這位農夫就是我們之前多次提到的機器指令。
機器指令中除了負責邏輯運算、執(zhí)行流控制、函數(shù)調用等指令外,還有一類指令,這類執(zhí)行只負責和內存打交道,典型的就是精簡指令集架構中的Load/Store機器指令,即內存讀寫指令(復雜指令集沒有單獨的內存讀寫指令)。
原來,從宏觀上看的話,存放在內存中的數(shù)據(jù),比如一個數(shù)組,可能會非常龐大,但是具體到代碼,每一個步驟操作的數(shù)據(jù)又會非常簡單,就像這樣:
- int* huge_arr = new int[1 * 1024* 1024 *1024];
我們創(chuàng)建了一個長度為1G的數(shù)組,每個int 4字節(jié),則這個數(shù)組的大小就是4GB,這顯然是一個很龐大的數(shù)組。
對于這樣的數(shù)據(jù),我們通常都會怎么使用呢?
最常見的情況可能是遍歷一邊,然后對每個字符進行一個簡單操作,這里以計算數(shù)組之和為例:
- long int sum = 0;
- for (int i = 0; i < 1 * 1024* 1024 *1024; i++) {
- sum += huge_arr[i];
- }
雖然整個數(shù)組多達4GB,但具體到每一步我們一次只能操作一個元素,就像這里的:
- sum += huge_arr[i];
這行代碼翻譯成機器指令可能是這樣的,我們假設此時i為100:
- load $r0 100($r2)
- add $r1 $r1 $r0
(注意,實際當中編譯器不會傻傻的生成100這樣的常數(shù),這里代碼僅用來方便講解問題)。
第一行指令中數(shù)組首地址存放在寄存器r2中,100($r2)表示數(shù)組首地址+100,這樣我們就能得到huge_arr[100]的地址了,然后將該地址中的值利用load指令加載到寄存器r0中。
第二行就簡單多了,r1寄存器中保存的是sum的值,該行指令執(zhí)行過后r1中的值就已經(jīng)加上了huge_arr[100]。
現(xiàn)在你應該能看出來了吧,雖然我們不能把整個數(shù)組加載到寄存器供CPU計算,但這其實是沒有必要的,因為我們一次只能操作數(shù)組中的一個元素,我們只需要把這一個元素加載到寄存器就足矣了。
對于其它復雜的數(shù)據(jù)結構也是同樣的道理,無論多么復雜的數(shù)據(jù),代碼對其一次的操作都是很簡單很微小的,這一微小的操作使用的基本元素都可以通過內存讀寫指令加載到寄存器,修改完后再寫回內存。
編譯器
現(xiàn)在你應該知道了為什么CPU內部那么少的寄存器能操作內存中龐大的數(shù)據(jù)結構,實際上由于內存中的數(shù)據(jù)要遠大于CPU寄存器的容量,因此編譯器必須精心挑選,好讓那些經(jīng)常使用的數(shù)據(jù)放到寄存器中的時間更長一點,這樣可以減少內存讀寫次數(shù)。
在上面的示例中,r2寄存器保存的是huge_arr這個數(shù)組在內存中的起始地址,那么這個數(shù)據(jù)應該放到寄存器中,因為后續(xù)遍歷到的每一個元素都要用到該地址,這項工作就是編譯器來完成的。
編譯器把那些經(jīng)常使用的數(shù)據(jù)放到寄存器,剩下的放到內存中,然后利用內存讀寫指令在寄存器和內存之間來回搬運數(shù)據(jù)。
總結
通過本文不難發(fā)現(xiàn),實際上我們沒有必要一次性把整個數(shù)據(jù)全部裝到CPU寄存器中,而是用到哪些才裝載哪些。
在最細粒度的操作中,依賴的操作數(shù)都可以直接加載到內存,這通常是由內存讀寫機器指令來完成的。
我是小風哥,希望這篇文章對大家理解CPU與寄存器有所幫助。
本文轉載自微信公眾號「碼農的荒島求生」,可以通過以下二維碼關注。轉載本文請聯(lián)系碼農的荒島求生公眾號。