天津營(yíng)銷型網(wǎng)站建設(shè)費(fèi)用友情鏈接的英文
CPU是如何執(zhí)行程序的?
程序執(zhí)行的基本過程

- 第一步,CPU 讀取「程序計(jì)數(shù)器」的值,這個(gè)值是指令的內(nèi)存地址,然后 CPU 的「控制單元」操作「地址總線」指定需要訪問的內(nèi)存地址,接著通知內(nèi)存設(shè)備準(zhǔn)備數(shù)據(jù),數(shù)據(jù)準(zhǔn)備好后通過「數(shù)據(jù)總線」將指令數(shù)據(jù)傳給 CPU,CPU 收到內(nèi)存?zhèn)鱽淼臄?shù)據(jù)后,將這個(gè)指令數(shù)據(jù)存入到「指令寄存器」。
- 第二步,「程序計(jì)數(shù)器」的值自增,表示指向下一條指令。這個(gè)自增的大小,由 CPU 的位寬決定,比如 32 位的 CPU,指令是 4 個(gè)字節(jié),需要 4 個(gè)內(nèi)存地址存放,因此「程序計(jì)數(shù)器」的值會(huì)自增 4;
- 第三步,CPU 分析「指令寄存器」中的指令,確定指令的類型和參數(shù),如果是計(jì)算類型的指令,就把指令交給「邏輯運(yùn)算單元」運(yùn)算;如果是存儲(chǔ)類型的指令,則交由「控制單元」執(zhí)行;
簡(jiǎn)單總結(jié):一個(gè)程序執(zhí)行的時(shí)候,CPU 會(huì)根據(jù)程序計(jì)數(shù)器里的內(nèi)存地址,從內(nèi)存里面把需要執(zhí)行的指令讀取到指令寄存器里面執(zhí)行,然后程序計(jì)數(shù)器根據(jù)指令長(zhǎng)度自增,開始順序讀取下一條指令。
- 在指令寄存器中,CPU會(huì)分析指令寄存器中的指令,確定指令的類型和參數(shù),如果是計(jì)算類型的指令,就把指令交給「邏輯運(yùn)算單元」運(yùn)算;如果是存儲(chǔ)類型的指令,則交由「控制單元」執(zhí)行;
- 程序計(jì)數(shù)器自增的長(zhǎng)度與CPU位寬決定,比如 32 位的 CPU,指令是 4 個(gè)字節(jié),需要 4 個(gè)內(nèi)存地址存放,因此「程序計(jì)數(shù)器」的值會(huì)自增 4;
CPU 從程序計(jì)數(shù)器讀取指令、到執(zhí)行、再到下一條指令,這個(gè)過程會(huì)不斷循環(huán),直到程序執(zhí)行結(jié)束,這個(gè)不斷循環(huán)的過程被稱為 CPU 的指令周期。
-
馮諾依曼模型定義了計(jì)算機(jī)基本結(jié)構(gòu):運(yùn)算器、控制器、存儲(chǔ)器、輸入輸出設(shè)備。
-
內(nèi)存
我們的程序和數(shù)據(jù)都是存儲(chǔ)在內(nèi)存,存儲(chǔ)的區(qū)域是線性的。
在計(jì)算機(jī)數(shù)據(jù)存儲(chǔ)中,存儲(chǔ)數(shù)據(jù)的基本單位是字節(jié)(byte),1 字節(jié)等于 8 位(8 bit)。每一個(gè)字節(jié)都對(duì)應(yīng)一個(gè)內(nèi)存地址。
-
中央處理器(CPU)
中央處理器也就是我們常說的 CPU,32 位和 64 位 CPU 最主要區(qū)別在于一次能計(jì)算多少字節(jié)數(shù)據(jù)。這里的32位和64位,通常表示CPU的位寬。
- 32 位 CPU 一次可以計(jì)算 4 個(gè)字節(jié);(4個(gè)字節(jié)就是32位 4 * 8)
- 64 位 CPU 一次可以計(jì)算 8 個(gè)字節(jié);
CPU內(nèi)部還有一些組件,常見的有寄存器,控制單元和邏輯運(yùn)算單元
-
控制單元:負(fù)責(zé)控制CPU的工作。
-
邏輯運(yùn)算單元:負(fù)責(zé)運(yùn)算。
-
寄存器:存儲(chǔ)計(jì)算時(shí)的數(shù)據(jù),由于內(nèi)存離CPU太遠(yuǎn)了,而寄存器就在CPU內(nèi)部,計(jì)算速度更快。寄存器有以下幾種:
- 通用寄存器:存放需要運(yùn)算的數(shù)據(jù)。
- 程序計(jì)數(shù)器:存儲(chǔ)CPU要執(zhí)行的下一條指令的地址。
- 指令寄存器:存放當(dāng)前正在執(zhí)行的指令。
-
總線
負(fù)責(zé)各種設(shè)備之間的通信。比如CPU讀取內(nèi)存數(shù)據(jù)時(shí),要通過三個(gè)總線:
- 先通過地址總線來指定內(nèi)存的地址。
- 再通過控制總線來指定讀或?qū)懙拿睢?/li>
- 最后通過數(shù)據(jù)總線來傳遞數(shù)據(jù)。
-
線路位寬與CPU位寬
-
線路位寬:數(shù)據(jù)在線路中傳輸,其實(shí)是通過操作電壓,低電壓表示0,高電壓表示1。這樣一位一位進(jìn)行傳輸?shù)姆绞椒Q為串行,想要一次性多傳輸數(shù)據(jù),可以增加線路的位寬。比如CPU 想要操作內(nèi)存地址就需要地址總線,可以通過增加線路位寬的方式,增加CPU能操作的最大內(nèi)存地址數(shù)量。(注意,不是說同時(shí)操作的最大內(nèi)存地址數(shù)量)
- 如果地址總線有 2 條,那么能表示 00、01、10、11 這四種地址,所以 CPU 能操作的內(nèi)存地址最大數(shù)量為 4(2^2)個(gè)。那么,想要 CPU 操作 4G 大的內(nèi)存,那么就需要 32 條地址總線,因?yàn)?
2 ^ 32 = 4G
。(32位對(duì)應(yīng)有232個(gè)地址,對(duì)應(yīng)的內(nèi)存數(shù)是232 * 8bit=4Gbyte即4GB)
- 如果地址總線有 2 條,那么能表示 00、01、10、11 這四種地址,所以 CPU 能操作的內(nèi)存地址最大數(shù)量為 4(2^2)個(gè)。那么,想要 CPU 操作 4G 大的內(nèi)存,那么就需要 32 條地址總線,因?yàn)?
-
CPU位寬:CPU 的位寬最好不要小于線路位寬,否則工作起來會(huì)非常復(fù)雜且麻煩。如果計(jì)算的數(shù)額不超過 32 位數(shù)字的情況下,32 位和 64 位 CPU 之間沒什么區(qū)別的,只有當(dāng)計(jì)算超過 32 位數(shù)字的情況下,64 位的優(yōu)勢(shì)才能體現(xiàn)出來。
- 另外,32 位 CPU 最大只能操作 4GB 內(nèi)存,就算你裝了 8 GB 內(nèi)存條,也沒用。而 64 位 CPU 尋址范圍則很大,理論最大的尋址空間為
2^64
。
- 另外,32 位 CPU 最大只能操作 4GB 內(nèi)存,就算你裝了 8 GB 內(nèi)存條,也沒用。而 64 位 CPU 尋址范圍則很大,理論最大的尋址空間為
-
-
a = 1 + 2的執(zhí)行具體過程
-
程序—>匯編語(yǔ)言—>計(jì)算機(jī)指令
CPU 是不認(rèn)識(shí)
a = 1 + 2
這個(gè)字符串,這些字符串只是方便我們程序員認(rèn)識(shí),要想這段程序能跑起來,還需要把整個(gè)程序翻譯成匯編語(yǔ)言的程序,這個(gè)過程稱為編譯成匯編代碼。針對(duì)匯編代碼,我們還需要用匯編器翻譯成機(jī)器碼,這些機(jī)器碼由 0 和 1 組成的機(jī)器語(yǔ)言,這一條條機(jī)器碼,就是一條條的計(jì)算機(jī)指令,這個(gè)才是 CPU 能夠真正認(rèn)識(shí)的東西。 -
程序編譯過程中,編譯器分析代碼,發(fā)現(xiàn)1和2是數(shù)據(jù),所以放在內(nèi)存中的「數(shù)據(jù)段」,編譯器會(huì)把a(bǔ) = 1 + 2翻譯成4條指令,存放到正文段中。
-
編譯完成后,具體執(zhí)行程序的時(shí)候,程序計(jì)數(shù)器會(huì)被設(shè)置為 0x100 地址,然后依次執(zhí)行這 4 條指令。
-
-
上面的例子中,由于是在 32 位 CPU 執(zhí)行的,因此一條指令是占 32 位大小,所以你會(huì)發(fā)現(xiàn)每條指令間隔 4 個(gè)字節(jié)。而數(shù)據(jù)的大小是根據(jù)你在程序中指定的變量類型,比如
int
類型的數(shù)據(jù)則占 4 個(gè)字節(jié),char
類型的數(shù)據(jù)則占 1 個(gè)字節(jié)。 -
你知道軟件的 32 位和 64 位之間的區(qū)別嗎?再來 32 位的操作系統(tǒng)可以運(yùn)行在 64 位的電腦上嗎?64 位的操作系統(tǒng)可以運(yùn)行在 32 位的電腦上嗎?如果不行,原因是什么?
-
64 位和 32 位軟件,實(shí)際上代表指令是 64 位還是 32 位的:
- 如果 32 位指令在 64 位機(jī)器上執(zhí)行,需要一套兼容機(jī)制,就可以做到兼容運(yùn)行了。但是如果 64 位指令在 32 位機(jī)器上執(zhí)行,就比較困難了,因?yàn)?32 位的寄存器存不下 64 位的指令;
- 操作系統(tǒng)其實(shí)也是一種程序,我們也會(huì)看到操作系統(tǒng)會(huì)分成 32 位操作系統(tǒng)、64 位操作系統(tǒng),其代表意義就是操作系統(tǒng)中程序的指令是多少位,比如 64 位操作系統(tǒng),指令也就是 64 位,因此不能裝在 32 位機(jī)器上。
總之,硬件的 64 位和 32 位指的是 CPU 的位寬,軟件的 64 位和 32 位指的是指令的位寬。
-
-
CPU時(shí)鐘頻率:1GHz表示該CPU的時(shí)鐘頻率是1G,表示1秒會(huì)發(fā)出1G次數(shù)的脈沖信號(hào),每一次脈沖信號(hào)的高低電平就是一個(gè)時(shí)鐘周期。時(shí)鐘周期時(shí)間越短,CPU運(yùn)算的越快。
磁盤比內(nèi)存慢幾萬倍?
-
機(jī)械硬盤、固態(tài)硬盤、內(nèi)存這三個(gè)存儲(chǔ)器,到底和
CPU L1 Cache
相比速度差多少倍呢?-
CPU L1 Cache 隨機(jī)訪問延時(shí)是 1 納秒,內(nèi)存則是 100 納秒,所以 CPU L1 Cache 比內(nèi)存快 100 倍左右。
-
SSD 隨機(jī)訪問延時(shí)是 150 微秒,所以 CPU L1 Cache 比 SSD 快 150000 倍左右。
-
最慢的機(jī)械硬盤隨機(jī)訪問延時(shí)已經(jīng)高達(dá) 10 毫秒,CPU L1 Cache 比機(jī)械硬盤快 10000000 倍左右;
-
-
寄存器
寄存器用來存儲(chǔ)計(jì)算的數(shù)據(jù),是最靠近 CPU 的控制單元和邏輯計(jì)算單元的存儲(chǔ)器。
寄存器的價(jià)格很貴,數(shù)量通常在幾十到幾百之間,每個(gè)寄存器可以用來存儲(chǔ)一定的字節(jié)(byte)的數(shù)據(jù)。比如:
- 32 位 CPU 中大多數(shù)寄存器可以存儲(chǔ) 4 個(gè)字節(jié);
- 64 位 CPU 中大多數(shù)寄存器可以存儲(chǔ) 8 個(gè)字節(jié)。
寄存器的訪問速度非???#xff0c;一般要求在半個(gè) CPU 時(shí)鐘周期內(nèi)完成讀寫,CPU 時(shí)鐘周期跟 CPU 主頻息息相關(guān),比如 2 GHz 主頻的 CPU,那么它的時(shí)鐘周期就是 1/2G,也就是 0.5ns(納秒)。
-
CPU Cache
CPU Cache 用的是一種叫 SRAM(Static Random-Access Memory,靜態(tài)隨機(jī)存儲(chǔ)器) 的芯片。靜態(tài),說明有電時(shí)數(shù)據(jù)一直存在,但掉電數(shù)據(jù)丟失。
在 SRAM 里面,一個(gè) bit 的數(shù)據(jù),通常需要 6 個(gè)晶體管,所以 SRAM 的存儲(chǔ)密度不高,同樣的物理空間下,能存儲(chǔ)的數(shù)據(jù)是有限的,不過也因?yàn)?SRAM 的電路簡(jiǎn)單,所以訪問速度非??臁?/strong>
CPU 的高速緩存,通??梢苑譃?L1、L2、L3 三層高速緩存,也稱為一級(jí)緩存、二級(jí)緩存、三級(jí)緩存。
-
L1 高速緩存是每個(gè)CPU Core獨(dú)有的,訪問速度快,只需要 2~4 個(gè)時(shí)鐘周期。大小在幾十 KB 到幾百 KB 不等。
L1 高速緩存的指令和數(shù)據(jù)是分開存放的,所以 L1 高速緩存通常分成指令緩存和數(shù)據(jù)緩存。
-
L2 高速緩存同樣每個(gè) CPU 核心都有,但是 L2 比L1 距離 CPU 核心更遠(yuǎn),訪問速度則更慢,速度在 10~20 個(gè)時(shí)鐘周期。但大小比 L1 更大,通常大小在幾百 KB 到幾 MB不等,
-
L3 高速緩存通常是多個(gè) CPU 核心共用的,位置比 L2 高速緩存距離 CPU 核心 更遠(yuǎn),訪問速度相對(duì)也比較慢一些,訪問速度在 20~60 個(gè)時(shí)鐘周期。大小也會(huì)更大些,通常大小在幾 MB 到幾十 MB 不等。
-
-
內(nèi)存
內(nèi)存用的芯片和 CPU Cache 有所不同,它使用的是一種叫作 DRAM (Dynamic Random Access Memory,動(dòng)態(tài)隨機(jī)存取存儲(chǔ)器) 的芯片。動(dòng)態(tài),因?yàn)閿?shù)據(jù)會(huì)被存儲(chǔ)在電容里,電容會(huì)不斷漏電,所以需要定時(shí)刷新電容,才能保證數(shù)據(jù)不會(huì)被丟失。
相比 SRAM,DRAM 的密度更高,功耗更低,有更大的容量,而且造價(jià)比 SRAM 芯片便宜很多。存儲(chǔ)一個(gè) bit 數(shù)據(jù),只需要一個(gè)晶體管和一個(gè)電容就能存儲(chǔ)。
DRAM 的數(shù)據(jù)訪問電路和刷新電路都比 SRAM 更復(fù)雜,所以訪問的速度會(huì)更慢,內(nèi)存速度大概在 200~300 個(gè) 時(shí)鐘周期之間。
-
SSD/HDD硬盤
固態(tài)硬盤(Solid-state disk,SSD) 結(jié)構(gòu)和內(nèi)存類似。相比寄存器/Cache/內(nèi)存的優(yōu)點(diǎn)是斷電后數(shù)據(jù)還存在;缺點(diǎn)是讀寫速度很慢。
機(jī)械硬盤(Hard Disk Drive, HDD),通過物理讀寫的方式來訪問數(shù)據(jù)的,因此它訪問速度是非常慢的,它的速度比內(nèi)存慢 10W 倍左右。由于 SSD 的價(jià)格快接近機(jī)械硬盤了,因此機(jī)械硬盤已經(jīng)逐漸被 SSD 替代了。
-
存儲(chǔ)器的層次關(guān)系
CPU 并不會(huì)直接和每一種存儲(chǔ)器設(shè)備直接打交道,而是每一種存儲(chǔ)器設(shè)備只和它相鄰的存儲(chǔ)器設(shè)備打交道。比如,CPU Cache 的數(shù)據(jù)是從內(nèi)存加載過來的,寫回?cái)?shù)據(jù)的時(shí)候也只寫回到內(nèi)存,再?gòu)膬?nèi)存寫回到硬盤中。
- 另外,當(dāng) CPU 需要訪問內(nèi)存中某個(gè)數(shù)據(jù)的時(shí)候,如果寄存器有這個(gè)數(shù)據(jù),CPU 就直接從寄存器取數(shù)據(jù)即可,如果寄存器沒有這個(gè)數(shù)據(jù),CPU 就會(huì)查詢 L1 高速緩存,如果 L1 沒有,則查詢 L2 高速緩存,L2 還是沒有的話就查詢 L3 高速緩存,L3 依然沒有的話,才去內(nèi)存中取數(shù)據(jù),并把內(nèi)存中的數(shù)據(jù)讀入到 Cache 中,CPU 再?gòu)?CPU Cache 讀取數(shù)據(jù)。
不同的存儲(chǔ)器之間性能差距很大,構(gòu)造存儲(chǔ)器分級(jí)很有意義,分級(jí)的目的是要構(gòu)造緩存體系。這樣的訪問機(jī)制,跟我們使用「內(nèi)存作為硬盤的緩存」的邏輯是一樣的。
如何寫出讓CPU跑的更快的代碼?
-
CPU Cache有多快?
根據(jù)摩爾定律,CPU 的訪問速度每 18 個(gè)月就會(huì)翻倍,相當(dāng)于每年增長(zhǎng) 60% 左右,內(nèi)存的速度當(dāng)然也會(huì)不斷增長(zhǎng),但是增長(zhǎng)的速度遠(yuǎn)小于 CPU,平均每年只增長(zhǎng) 7% 左右。于是,CPU 與內(nèi)存的訪問性能的差距不斷拉大。到現(xiàn)在,一次內(nèi)存訪問所需時(shí)間是 200~300 多個(gè)時(shí)鐘周期,這意味著 CPU 和內(nèi)存的訪問速度已經(jīng)相差 200~300 多倍了。
為了彌補(bǔ) CPU 與內(nèi)存兩者之間的性能差異,就在 CPU 內(nèi)部引入了 CPU Cache,也稱高速緩存。
-
CPU Cache 數(shù)據(jù)結(jié)構(gòu)?
CPU Cache 是由很多個(gè) Cache Line 組成的,Cache Line (緩存塊)是 CPU 從內(nèi)存讀取數(shù)據(jù)的基本單位。
Cache Line 結(jié)構(gòu)
- 有效位(Valid bit):標(biāo)記對(duì)應(yīng)Cache Line 中數(shù)據(jù)是否有效的有效位。
- 組標(biāo)記(Tag):為了區(qū)分不同的內(nèi)存塊,在對(duì)應(yīng)的CPU Cache LIne中會(huì)存儲(chǔ)一個(gè)組標(biāo)記,用來記錄當(dāng)前CPU Cache Line 中存儲(chǔ)的數(shù)據(jù)對(duì)應(yīng)的內(nèi)存塊,區(qū)分不同的內(nèi)存塊。
- 數(shù)據(jù)(Data):從內(nèi)存加載過來的數(shù)據(jù)。
-
內(nèi)存地址與Cache Line的直接映射
CPU 訪問內(nèi)存數(shù)據(jù)時(shí),是一小塊一小塊數(shù)據(jù)讀取的,具體這一小塊數(shù)據(jù)的大小,取決于 coherency_line_size 的值,一般 64 字節(jié)。在內(nèi)存中,這一塊的數(shù)據(jù)我們稱為內(nèi)存塊(Block),讀取的時(shí)候我們要拿到數(shù)據(jù)所在內(nèi)存塊的地址。
一個(gè)內(nèi)存的訪問地址,包括組標(biāo)記、CPU Cache Line 索引、偏移量這三種信息,于是 CPU 就能通過這些信息,在 CPU Cache 中找到緩存的數(shù)據(jù)。
- 對(duì)于直接映射 Cache 采用的策略,就是把內(nèi)存塊的地址始終映射在一個(gè) CPU Cache Line 的地址,至于映射關(guān)系實(shí)現(xiàn)方式,則是使用取模運(yùn)算,取模運(yùn)算的結(jié)果就是內(nèi)存塊地址對(duì)應(yīng)的 CPU Cache Line(緩存塊) 的地址。
- 使用偏移量,可以在CPU Cache Line 中的數(shù)據(jù)塊找到對(duì)應(yīng)的字。
-
CPU Cache 讀寫過程?
-
根據(jù)內(nèi)存地址中索引信息,計(jì)算在 CPU Cache 中的索引,也就是找出對(duì)應(yīng)的 CPU Cache Line 的地址;
-
找到對(duì)應(yīng) CPU Cache Line 后,判斷 CPU Cache Line 中的有效位,確認(rèn) CPU Cache Line 中數(shù)據(jù)是否是有效的,如果是無效的,CPU 就會(huì)直接訪問內(nèi)存,并重新加載數(shù)據(jù),如果數(shù)據(jù)有效,則往下執(zhí)行;
-
對(duì)比內(nèi)存地址中組標(biāo)記和 CPU Cache Line 中的組標(biāo)記,確認(rèn) CPU Cache Line 中的數(shù)據(jù)是我們要訪問的內(nèi)存數(shù)據(jù),如果不是的話,CPU 就會(huì)直接訪問內(nèi)存,并重新加載數(shù)據(jù),如果是的話,則往下執(zhí)行;
-
根據(jù)內(nèi)存地址中偏移量信息,從 CPU Cache Line 的數(shù)據(jù)塊中,讀取對(duì)應(yīng)的字。
- CPU 在從 CPU Cache 讀取數(shù)據(jù)的時(shí)候,并不是讀取 CPU Cache Line 中的整個(gè)數(shù)據(jù)塊,而是讀取 CPU 所需要的一個(gè)數(shù)據(jù)片段,這樣的數(shù)據(jù)統(tǒng)稱為一個(gè)字(Word)。
-
-
寫出讓CPU跑得更快的程序
CPU操作L1 Cache的速度是很快的,提升CPU運(yùn)行速度,可以提升訪問L1 Cache的速度。
那么L1 Cache 通常分為「數(shù)據(jù)緩存」和「指令緩存」,因此要分別提高「數(shù)據(jù)緩存」和「指令緩存」的緩存命中率。
-
提升數(shù)據(jù)緩存的命中率
CPU會(huì)一次從內(nèi)存中加載多少元素到 CPU Cache ,可以在Linux 里通過 coherency_line_size 配置查看 它的大小,通常是 64 個(gè)字節(jié)。
如果遇到遍歷數(shù)組之類的情況時(shí),按照內(nèi)存布局順序訪問,將可以有效的利用 CPU Cache 帶來的好處,這樣我們代碼的性能就會(huì)得到很大的提升。
-
提升指令緩存的命中率
使用顯式分支預(yù)測(cè)工具,如果分支預(yù)測(cè)可以預(yù)測(cè)到接下來要執(zhí)行 if 里的指令,還是 else 指令的話,就可以「提前」把這些指令放在指令緩存中,這樣 CPU 可以直接從 Cache 讀取到指令,于是執(zhí)行速度就會(huì)很快。
- 比如:如果你肯定代碼中的 if 中的表達(dá)式判斷為 true 的概率比較高,我們可以使用顯示分支預(yù)測(cè)工具,比如在 C/C++ 語(yǔ)言中,如果 if 條件為 ture 的概率大,則可以用 likely 宏把 if 里的表達(dá)式包裹起來。
還可以提升多核CPU的緩存命中率
-
L2 Cache和L1 Cache 是每個(gè)核心獨(dú)有的,如果一個(gè)線程在不同核心來回切換,各個(gè)核心的緩存命中率就會(huì)受到影響,可以把線程綁定到某一個(gè)CPU核心上。
在 Linux 上提供了 sched_setaffinity 方法,來實(shí)現(xiàn)將線程綁定到某個(gè) CPU 核心這一功能。
-
整理自:小林coding