行與單多線程性能解析)
大家好我是老周。這幾天大家都在討論小米新 CPU 的曝光消息特別是“Xring O3”這個(gè)名字以及“單線程媲美蘋(píng)果多線程大幅領(lǐng)先”的說(shuō)法。很多讀者私信問(wèn)我怎么看這里先統(tǒng)一回復(fù)目前能看到的信息基本都是爆料和早期工程分析的階段沒(méi)有任何官方規(guī)格書(shū)所以這篇文章不打算對(duì)具體跑分和架構(gòu)參數(shù)做“實(shí)錘”式解讀。我更想借著“Xring O3”這個(gè)名字把 CPU 核心設(shè)計(jì)里最關(guān)鍵、也最容易讓普通開(kāi)發(fā)者混淆的幾個(gè)概念講清楚O3 是什么水平的設(shè)計(jì)Xring 在整顆芯片里到底干什么單線程與多線程性能分別由什么決定以及當(dāng)我們看到一張 CPU 天梯圖時(shí)應(yīng)該如何正確解讀。無(wú)論最后小米這顆芯片的實(shí)測(cè)結(jié)果如何讀懂這些底層原理比你記住任何一串跑分?jǐn)?shù)字都更有價(jià)值。1. 背景為什么“新 CPU 單線程 多線程”值得關(guān)注先把這個(gè)話題放到一個(gè)大背景里看。消費(fèi)級(jí) CPU 市場(chǎng)過(guò)去幾年的競(jìng)爭(zhēng)焦點(diǎn)頭一次變得這么清晰以前大家比的是核心數(shù)和主頻這幾年變成了比單核效能、能效比、緩存架構(gòu)和互聯(lián)設(shè)計(jì)?!皢尉€程媲美蘋(píng)果”這句話之所以能引起這么大討論是因?yàn)樘O(píng)果的 CPU 核心在單線程性能上長(zhǎng)期處于標(biāo)桿地位ARM 公版核心和 x86 陣營(yíng)都在努力追趕。如果國(guó)產(chǎn)芯片真的能在單核性能上逼近甚至追平蘋(píng)果那說(shuō)明其微架構(gòu)設(shè)計(jì)已經(jīng)有了質(zhì)的變化而不是單純靠堆核心數(shù)量。“多線程性能大幅領(lǐng)先”則說(shuō)的是另一條路線。單線程再?gòu)?qiáng)也有物理極限當(dāng)你有足夠多的核心并且互聯(lián)總線能把各個(gè)核心高效協(xié)同起來(lái)多線程吞吐就會(huì)有明顯優(yōu)勢(shì)。這里的核心不在于“有幾顆核”而在于核心之間的數(shù)據(jù)通路是否夠?qū)挕蚩?、夠穩(wěn)定。而這恰恰是“Xring”這個(gè)名字可能要發(fā)揮作用的領(lǐng)域。對(duì)開(kāi)發(fā)者而言理解這種變化的意義在于你寫(xiě)的代碼能不能跑滿這顆 CPU取決于你的應(yīng)用是單線程敏感型還是多線程吞吐型。如果你還停留在“CPU 性能等于主頻乘核心數(shù)”這個(gè)粗糙模型里那你在做性能優(yōu)化和選型的時(shí)候會(huì)走很多彎路。2. 從 O3 說(shuō)起亂序執(zhí)行到底解決了什么問(wèn)題2.1 從“按順序執(zhí)行”到“亂序執(zhí)行”先把 O3 的全稱說(shuō)清楚O3即 Out-of-Order亂序執(zhí)行。早期 CPU 執(zhí)行指令是嚴(yán)格按程序順序來(lái)的取一條、譯碼、執(zhí)行、寫(xiě)回再來(lái)下一條。這種叫 in-order 順序執(zhí)行。它的優(yōu)點(diǎn)是設(shè)計(jì)簡(jiǎn)單、功耗低、面積小缺點(diǎn)是執(zhí)行單元經(jīng)常閑著。舉例來(lái)說(shuō)一條指令要讀取內(nèi)存中的數(shù)據(jù)而內(nèi)存延遲可能高達(dá)幾十甚至上百個(gè)時(shí)鐘周期。如果 CPU 按順序執(zhí)行下一條指令哪怕不依賴這個(gè)數(shù)據(jù)也得傻等內(nèi)存返回后才能動(dòng)手。這就造成了大量流水線氣泡單線程性能上不去。亂序執(zhí)行的核心思路就是在不改變程序最終語(yǔ)義的前提下允許后面的指令越過(guò)前面的指令先執(zhí)行。說(shuō)得直白一點(diǎn)CPU 會(huì)在內(nèi)部把指令打亂順序把能先算的先算了把要等數(shù)據(jù)的操作放到一邊等數(shù)據(jù)到了再補(bǔ)上最后按原始程序順序提交結(jié)果。這樣一來(lái)執(zhí)行單元的空閑時(shí)間大大減少單線程 IPC每時(shí)鐘周期執(zhí)行指令數(shù)就能顯著提升。不過(guò)要提醒一句亂序執(zhí)行不是萬(wàn)能的它解決的是“指令級(jí)并行”的挖掘問(wèn)題。如果你的代碼本身是一條又長(zhǎng)又依賴的串行鏈那即便是再激進(jìn)的 O3 引擎也幫不上太多忙。2.2 O3 處理器的關(guān)鍵組成ROB、調(diào)度器、寄存器重命名要理解 O3 是怎么實(shí)現(xiàn)的得認(rèn)識(shí)幾個(gè)核心部件。第一個(gè)是 ROBReorder Buffer重排序緩沖。它負(fù)責(zé)記錄每條指令的原始順序。指令亂序執(zhí)行完之后ROB 會(huì)確保按原始程序順序提交結(jié)果從而保證程序語(yǔ)義不被破壞。一旦發(fā)生分支預(yù)測(cè)錯(cuò)誤ROB 還能把已經(jīng)改動(dòng)的狀態(tài)回滾。第二個(gè)是調(diào)度器Scheduler/Reservation Station。調(diào)度器負(fù)責(zé)看哪些指令的操作數(shù)已經(jīng)準(zhǔn)備好、執(zhí)行單元是否空閑然后挑選可以發(fā)射的指令發(fā)給對(duì)應(yīng)的執(zhí)行單元。后端有幾套執(zhí)行單元調(diào)度器就得盡量讓它們“雨露均沾”誰(shuí)先具備條件誰(shuí)先執(zhí)行。第三個(gè)是寄存器重命名Register Renaming。這個(gè)名字看起來(lái)抽象其實(shí)解決的是“假依賴”問(wèn)題。舉個(gè)例子程序里連續(xù)使用同一個(gè)寄存器但前后之間并沒(méi)有真正數(shù)據(jù)關(guān)聯(lián)只是復(fù)用了寄存器編號(hào)而已。如果不做處理后一條指令會(huì)被迫等待前一條釋放寄存器這就是寫(xiě)后寫(xiě)WAW和寫(xiě)后讀WAR等假依賴。寄存器重命名會(huì)偷偷把程序里同一個(gè)邏輯寄存器映射到多個(gè)物理寄存器上從而解除這些假依賴讓更多指令能夠并行執(zhí)行。這三個(gè)部件加上分支預(yù)測(cè)器和各級(jí)緩存構(gòu)成了現(xiàn)代高性能 O3 核心的基本骨架。所以當(dāng)爆料里提到“Xring O3”的時(shí)候可以合理推測(cè)這是一顆具備完整亂序執(zhí)行能力的核心而不是以前那種偏簡(jiǎn)單順序執(zhí)行的設(shè)計(jì)。2.3 為什么單線程性能往往看 O3 是否激進(jìn)單線程性能可以大致拆成三個(gè)維度的乘法關(guān)系單線程性能 主頻 × IPC × 實(shí)際利用率主頻大家比較好理解就是 CPU 每秒能跑多少個(gè)周期。IPC 則是每個(gè)時(shí)鐘周期能完成多少條指令這直接由微架構(gòu)決定。O3 窗口越大、調(diào)度器越聰明、緩存命中率越高IPC 就越高。實(shí)際利用率則取決于分支預(yù)測(cè)準(zhǔn)不準(zhǔn)、指令緩存夠不夠大、同時(shí)訪問(wèn)內(nèi)存時(shí)能不能利用上內(nèi)存級(jí)并行。“媲美蘋(píng)果”這類說(shuō)法本質(zhì)上是在說(shuō) IPC 和利用率達(dá)到了一個(gè)非常高的水準(zhǔn)。后文我會(huì)給出結(jié)論不一定。這取決于實(shí)現(xiàn)細(xì)節(jié)——包括 L1 指令緩存的容量、L2 TLB快表的大小、預(yù)取器和譯碼器的設(shè)計(jì)等。真正可用的數(shù)據(jù)要等官方發(fā)布后才可通過(guò) SPEC CPU 等標(biāo)準(zhǔn)測(cè)試獲得而不是通過(guò)手機(jī)上的幾個(gè)跑分 App 就能看出結(jié)果。3. Xring不只是“幾核拼在一起”3.1 片上互聯(lián)的演進(jìn)總線 - 環(huán)形 - 網(wǎng)格“Xring”這個(gè)名字最直觀的理解是“某種環(huán)形總線結(jié)構(gòu)”。這里不討論具體實(shí)現(xiàn)細(xì)節(jié)而是介紹一下環(huán)形總線在 CPU 中的定位。早期多核 CPU 內(nèi)部使用的是共享總線架構(gòu)所有核心、緩存、內(nèi)存控制器都掛在同一條總線上。優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單缺點(diǎn)是總線帶寬有限核心一多就會(huì)出現(xiàn)爭(zhēng)搶擴(kuò)展性很差。后來(lái)業(yè)界開(kāi)始用環(huán)形總線Ring Bus替代共享總線。環(huán)形總線可以看作 CPU 內(nèi)部的一條環(huán)形車道每個(gè)核心、每塊緩存、每個(gè) I/O 組件都在環(huán)上有一個(gè)“站點(diǎn)”數(shù)據(jù)像在環(huán)形車道上按固定方向跑。英特爾在酷睿系列上采用的是雙向環(huán)形總線一個(gè)環(huán)順時(shí)針走一個(gè)環(huán)逆時(shí)針走訪問(wèn)數(shù)據(jù)時(shí)選擇更短的方向從而減少延遲。相比共享總線環(huán)形總線的優(yōu)勢(shì)很明顯帶寬更高每個(gè)站點(diǎn)都有獨(dú)立接入點(diǎn)擴(kuò)展性更好同時(shí)由于數(shù)據(jù)傳遞是點(diǎn)到點(diǎn)的避免了多個(gè)核心同時(shí)占用總線造成的沖突。它的局限性在于站點(diǎn)越多數(shù)據(jù)繞環(huán)一周的平均距離就越長(zhǎng)訪問(wèn)遠(yuǎn)端緩存的延遲會(huì)隨之增加。所以當(dāng)核心數(shù)量特別多時(shí)不少?gòu)S商又改用了網(wǎng)格互聯(lián)Mesh比如英特爾在 Skylake-SP 及之后的至強(qiáng)可擴(kuò)展處理器上就大量采用 Mesh 架構(gòu)。環(huán)形總線與網(wǎng)格互聯(lián)的選擇本質(zhì)上是延遲與吞吐、面積與功耗之間的權(quán)衡。環(huán)形總線適合核心數(shù)量適中且需要低延遲訪問(wèn)的場(chǎng)景網(wǎng)格互聯(lián)適合核心數(shù)量很大、需要更多并行帶寬的場(chǎng)景。3.2 環(huán)形總線與多線程吞吐的關(guān)系為什么一顆 CPU 的多線程性能會(huì)和內(nèi)部互聯(lián)結(jié)構(gòu)有關(guān)舉個(gè)簡(jiǎn)單例子如果你有 8 個(gè)核心每個(gè)核心都在獨(dú)立算自己的數(shù)互不干擾那么互聯(lián)結(jié)構(gòu)的影響可能不大但如果你的應(yīng)用是多線程協(xié)作型的比如線程 A 算完中間結(jié)果要交給線程 B線程 B 又要讀取共享緩存中的某些數(shù)據(jù)那么數(shù)據(jù)在核心之間傳遞的速度就直接決定了整體吞吐。此時(shí)環(huán)形總線的帶寬和延遲就很重要了。帶寬決定了同一時(shí)間內(nèi)能傳遞多少數(shù)據(jù)延遲決定了一次數(shù)據(jù)傳遞要等多少個(gè)周期。環(huán)形總線設(shè)計(jì)得好可以讓共享緩存訪問(wèn)更快速、緩存一致性開(kāi)銷更低、多線程協(xié)作的效率更高。所以“多線程性能大幅領(lǐng)先”如果成立多半不只是核心數(shù)量多而是互聯(lián)結(jié)構(gòu)跟核心微架構(gòu)配合得很好。3.3 從互聯(lián)結(jié)構(gòu)理解“緩存一致性”這個(gè)隱形開(kāi)銷講多線程就繞不開(kāi)緩存一致性。每個(gè) CPU 核心都有自己的 L1、L2 緩存多個(gè)核心共享 L3 緩存。同一個(gè)數(shù)據(jù)可能同時(shí)存在于多個(gè)核心的緩存中如果一個(gè)核心修改了它其他核心必須知道這個(gè)變化否則就會(huì)出現(xiàn)邏輯錯(cuò)誤。這個(gè)讓所有核心的緩存視圖保持一致的機(jī)制叫做緩存一致性協(xié)議最常見(jiàn)的實(shí)現(xiàn)是 MESI 協(xié)議及其變種。一致性協(xié)議依賴互聯(lián)結(jié)構(gòu)來(lái)廣播無(wú)效化消息、轉(zhuǎn)發(fā)數(shù)據(jù)請(qǐng)求。如果環(huán)形總線設(shè)計(jì)得好無(wú)效化消息能快速到達(dá)目標(biāo)核心整體性能損失就小如果互聯(lián)設(shè)計(jì)得不好哪怕核心數(shù)再多多線程跑起來(lái)也會(huì)出現(xiàn)大量緩存一致性流量導(dǎo)致性能不升反降。這也能解釋為什么很多人跑多線程程序時(shí)發(fā)現(xiàn)核心利用率很高但并沒(méi)有線性加速——很大部分開(kāi)銷都消耗在緩存一致性和鎖競(jìng)爭(zhēng)上了。所以多線程性能不只是操作系統(tǒng)調(diào)度器的事芯片底層的緩存感知設(shè)計(jì)決定了你的線程之間的數(shù)據(jù)共享到底有多快。4. 單線程 vs 多線程為什么單獨(dú)看任何一個(gè)都不夠4.1 單線程性能的決定因素指令延遲是核心指標(biāo)在單線程場(chǎng)景里最核心的指標(biāo)是指令延遲也就是一條指令從進(jìn)入到執(zhí)行完成需要多少時(shí)間。具體來(lái)說(shuō)單線程性能取決于以下因素分支預(yù)測(cè)器質(zhì)量if/for/while 天天都在用分支預(yù)測(cè)一旦猜錯(cuò)流水線前面干的所有活都要作廢?,F(xiàn)代高性能核心的分支預(yù)測(cè)準(zhǔn)確率通常能到 95% 以上但誤差哪怕是 1%在深度流水線下都是不小的性能損失。緩存命中率L1 緩存命中通常只需要幾個(gè)周期L2 十幾個(gè)周期L3 幾十個(gè)周期主存要幾百個(gè)周期。如果代碼數(shù)據(jù)訪問(wèn)完全沒(méi)有局部性那就算核心再?gòu)?qiáng)也只能被內(nèi)存延遲拖死。亂序執(zhí)行窗口O3 引擎能同時(shí)跟蹤多少條指令、能調(diào)度多少條在途指令決定了執(zhí)行單元能否被充分填滿。所以嚴(yán)格意義上講單線程性能并不只看主頻高低。一個(gè)主頻 3.0GHz 但 IPC 很高的核心完全可能超過(guò)主頻 3.5GHz 但 IPC 較低的核心。4.2 多線程性能的決定因素吞吐量受制于資源總量多線程性能靠的是并行吞吐量思路完全不同。在理想情況下n 個(gè)核心跑 n 條線程性能是單線程的 n 倍。但現(xiàn)實(shí)世界中這個(gè)比例幾乎不可能達(dá)到原因主要有三個(gè)每個(gè)核心仍然要共享內(nèi)存帶寬、L3 緩存等資源線程同時(shí)訪問(wèn)同一內(nèi)存區(qū)域時(shí)會(huì)出現(xiàn)爭(zhēng)搶。線程之間的同步操作、鎖競(jìng)爭(zhēng)、原子指令都會(huì)產(chǎn)生串行化的等待時(shí)間。不同應(yīng)用的可并行度不一樣有的能輕松擴(kuò)展到幾十個(gè)線程有的在 4 線程時(shí)就基本瓶頸了。多線程性能通常用 SPECrate、Cinebench 多核渲染、HandBrake 視頻編碼這類測(cè)試來(lái)衡量因?yàn)樗鼈兊牟⑿卸群芨吣鼙容^充分地壓榨出多核性能。但要注意這些測(cè)試結(jié)果是“有條件的極限能力”。真實(shí)應(yīng)用如果并行度不高那么多核再?gòu)?qiáng)也未必能從中受益。4.3 熱門(mén)語(yǔ)言與框架里的“多線程”誤區(qū)這部分值得單獨(dú)說(shuō)一下因?yàn)楹芏嗳舜_實(shí)把它們弄混了。Redis 是單線程還是多線程答默認(rèn)情況下Redis 的命令處理核心是單線程但 Redis 6.0 之后引入了多線程 I/O用于處理網(wǎng)絡(luò)讀寫(xiě)。所以準(zhǔn)確說(shuō)法是“核心命令處理單線程網(wǎng)絡(luò) I/O 可以多線程”。很多人只回答“單線程”容易被面試官認(rèn)為知識(shí)沒(méi)有跟上版本。Python 的多線程為什么跑不滿 CPU因?yàn)?CPython 解釋器有一個(gè)全局解釋器鎖GIL同一時(shí)刻只允許一個(gè)線程執(zhí)行 Python 字節(jié)碼。所以 CPU 密集型任務(wù)用 Python 多線程往往得不到理想加速但是 I/O 密集型任務(wù)比如網(wǎng)絡(luò)請(qǐng)求、文件讀寫(xiě)多線程可以顯著改善響應(yīng)速度因?yàn)樵诰€程等待 I/O 時(shí)會(huì)釋放 GIL。Java 的多線程則沒(méi)有 GIL 這種限制。Java 的線程模型直接映射到操作系統(tǒng)線程可以通過(guò)線程池、并發(fā)工具類來(lái)實(shí)現(xiàn)真正的多核并行。但在高并發(fā)場(chǎng)景下要小心鎖競(jìng)爭(zhēng)、偽共享False Sharing和線程上下文切換開(kāi)銷。偽共享是個(gè)很容易踩的坑兩個(gè)線程各自修改不同的變量但這兩個(gè)變量恰好在同一個(gè)緩存行里每次修改都導(dǎo)致緩存行在兩個(gè)核心之間來(lái)回傳遞性能大幅下降。解決方式是做緩存行填充或讓變量錯(cuò)開(kāi)分布。C 的多線程是原生支持且性能可控的使用 std::thread 或線程池可以很自由地控制線程行為但同時(shí)也要求開(kāi)發(fā)者對(duì)內(nèi)存模型有較深理解否則很容易出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)和難以查明的并發(fā) bug。這里想強(qiáng)調(diào)的是多線程性能是否跑得滿除了看 CPU 有多少核心還要看你的語(yǔ)言運(yùn)行時(shí)是怎么調(diào)度、怎么加鎖、怎么處理共享內(nèi)存的。你不能拿一份 GIL 受限的 Python 測(cè)試結(jié)果去判斷 CPU 多核性能不行這是兩碼事。4.4 如何用標(biāo)準(zhǔn)工具評(píng)估 CPU 性能常見(jiàn)評(píng)測(cè)方式有以下幾類SPEC CPU 2017業(yè)界認(rèn)可度最高的 CPU 性能基準(zhǔn)之一包含整數(shù)和浮點(diǎn)兩套測(cè)試分 speed單任務(wù)運(yùn)行時(shí)間和 rate多任務(wù)吞吐量?jī)煞N模式。前面提到的“單線程媲美蘋(píng)果”通常就是在說(shuō) SPECspeed 類分?jǐn)?shù)。Cinebench基于 Cinema 4D 的渲染測(cè)試單核和多核分?jǐn)?shù)都很直觀是消費(fèi)級(jí) CPU 天梯圖常用的數(shù)據(jù)來(lái)源。Geekbench 系列跨平臺(tái)基準(zhǔn)測(cè)試覆蓋場(chǎng)景豐富適合快速對(duì)比手機(jī)和電腦 CPU但其分?jǐn)?shù)在學(xué)術(shù)界和服務(wù)器領(lǐng)域參考價(jià)值有限。實(shí)際業(yè)務(wù)負(fù)載壓測(cè)最可靠但仍然受限于環(huán)境。比如你對(duì) Redis、MySQL、Nginx 做壓測(cè)要看 QPS、延遲和 CPU 占用率。這種測(cè)試的優(yōu)點(diǎn)是貼近生產(chǎn)環(huán)境缺點(diǎn)是不同軟硬件配置下的結(jié)果差異很大很難橫向?qū)Ρ?。此外還有 CPU 天梯圖比如服務(wù)器 CPU 天梯圖這種圖一般把 CPU 按綜合性能排成等級(jí)??磮D時(shí)需要留意它用的是單核分?jǐn)?shù)還是多核分?jǐn)?shù)、測(cè)試版本號(hào)是哪一個(gè)、是否包含功耗與價(jià)格因素。否則同型號(hào) CPU 在不同天梯圖上的位次相差很遠(yuǎn)導(dǎo)致用戶困惑甚至產(chǎn)生誤解。5. 假如有新 CPU 面世我們?cè)撽P(guān)注哪些關(guān)鍵指標(biāo)5.1 不應(yīng)先看跑分先看三點(diǎn)IPC、功耗、生態(tài)對(duì)于“Xring O3”這種名稱我們?cè)谌鄙俟俜綌?shù)據(jù)時(shí)可以先建立一個(gè)通用評(píng)價(jià)框架之后等真機(jī)或評(píng)測(cè)出爐再按這個(gè)框架做比對(duì)。第一看 IPC 是否真的提升。如果新核心只是靠提升主頻來(lái)拉高單線程成績(jī)那它會(huì)面臨較明顯的功耗和散熱壓力。如果頻率基本不變而 IPC 明顯提升則說(shuō)明亂序執(zhí)行引擎、緩存系統(tǒng)、分支預(yù)測(cè)等微架構(gòu)組件確實(shí)做了改進(jìn)。這才是真正有含金量的進(jìn)步。第二看能效比。芯片設(shè)計(jì)不能只看峰值性能還要看達(dá)到該性能需要多少功耗。同樣一顆核心如果 5W 功耗下能跑出 80% 的最大性能那放在移動(dòng)設(shè)備里就會(huì)帶來(lái)更好的續(xù)航和溫控表現(xiàn)如果必須頂著 10W 才能跑滿持續(xù)負(fù)載下可能會(huì)迅速降頻。第三看生態(tài)兼容性。指令集、軟件生態(tài)、操作系統(tǒng)支持、編譯器優(yōu)化這些決定了你買(mǎi)的 CPU 能不能真正跑起需要的軟件。如果編譯器還沒(méi)為它做調(diào)度優(yōu)化那么即便微架構(gòu)很先進(jìn)在部分傳統(tǒng) benchmark 上也可能拿不出亮眼成績(jī)。開(kāi)發(fā)者尤其要關(guān)注這一塊因?yàn)樵購(gòu)?qiáng)的 CPU 也得通過(guò)工具鏈和運(yùn)行時(shí)才能發(fā)揮出來(lái)。5.2 Benchmark 結(jié)果受版本與場(chǎng)景影響很大我之前遇到過(guò)不少朋友拿著 CPU 天梯圖去找性能問(wèn)題結(jié)果發(fā)現(xiàn)完全對(duì)不上。這里需要說(shuō)一下原因天梯圖上的分?jǐn)?shù)是特定軟硬件環(huán)境下的產(chǎn)物不代表所有工作負(fù)載的通用表現(xiàn)。同樣是 SPEC CPU 2017int speed 和 int rate 反映的是不同的性能維度。前者側(cè)重單任務(wù)、單線程后者側(cè)重多任務(wù)、多線程吞吐。你拿 int speed 的排名去預(yù)測(cè)多核編譯速度自然不準(zhǔn)。再比如 Cinebench它優(yōu)化得非常好能調(diào)用所有核心并利用 SIMD 指令集。如果你的目標(biāo)應(yīng)用是一個(gè)優(yōu)化較差的單線程程序那 Cinebench 多核高分對(duì)你沒(méi)有任何參考意義。所以正確姿勢(shì)是先明確 CPU 的典型負(fù)載是單線程敏感型還是多線程并行型再選對(duì)應(yīng)的基準(zhǔn)數(shù)據(jù)最后用真實(shí)負(fù)載驗(yàn)證不要一概而論。5.3 另一個(gè)容易被忽視的指標(biāo)內(nèi)存子系統(tǒng)所有 CPU 性能最終都離不開(kāi)內(nèi)存子系統(tǒng)。即使核心計(jì)算能力再?gòu)?qiáng)如果內(nèi)存帶寬不足、延遲過(guò)高數(shù)據(jù)進(jìn)不來(lái)出不去執(zhí)行單元照樣空轉(zhuǎn)。評(píng)估 CPU 時(shí)建議同時(shí)關(guān)注內(nèi)存通道數(shù)雙通道、四通道還是八通道。相同頻率下通道越多理論帶寬越高多線程拷貝類任務(wù)受益明顯。內(nèi)存頻率和延遲DDR4 和 DDR5 的差異主要在建模式上頻率越高、延遲越低對(duì) IPC 越有利。緩存層級(jí)與容量L3 緩存越大能緩沖的工作集越大多核間共享數(shù)據(jù)的命中率越高。在做服務(wù)器選型時(shí)內(nèi)存帶寬尤其重要。比如在高并發(fā) Web 服務(wù)、大數(shù)據(jù)分析等場(chǎng)景CPU 計(jì)算時(shí)間往往只占一小段大量時(shí)間都花在等待內(nèi)存數(shù)據(jù)上。如果只看 CPU 型號(hào)不看內(nèi)存配置很容易出現(xiàn)“換了更強(qiáng)的 CPU 但業(yè)務(wù)性能沒(méi)提升”的困惑。6. 實(shí)際開(kāi)發(fā)與運(yùn)維中的高頻 CPU 場(chǎng)景6.1 硬件信息與狀態(tài)查詢很多開(kāi)發(fā)者剛接觸 Linux 服務(wù)器時(shí)第一件事就是想知道這臺(tái)機(jī)器的 CPU 是什么。在 Linux 下可以用以下命令查看lscpu它會(huì)輸出 CPU 架構(gòu)、核心數(shù)、線程數(shù)、主頻、緩存大小、支持的指令集等信息。想看得更細(xì)可以查看 /proc/cpuinfocat /proc/cpuinfo | grep model name | uniq cat /proc/cpuinfo | grep cpu cores | uniq cat /proc/cpuinfo | grep siblings | uniq其中 “cpu cores” 表示物理核心數(shù)“siblings” 表示邏輯處理器總數(shù)。兩者相除可以得到每個(gè)物理核心支持的線程數(shù)。比如 siblings 為 16、cpu cores 為 8說(shuō)明啟用了超線程每個(gè)物理核心對(duì)應(yīng)兩個(gè)邏輯處理器。如果是在 Windows 上最早在任務(wù)管理器里能看到邏輯處理器數(shù)如果需要更詳細(xì)的架構(gòu)信息可以用 CPU-Z。這里還要提一個(gè)常見(jiàn)問(wèn)題虛擬機(jī)里看到的 CPU 信息往往不準(zhǔn)確。如果宿主機(jī)的 CPU 沒(méi)有啟用嵌套虛擬化或沒(méi)有正確透?jìng)魈摂M機(jī)看到的可能是一個(gè)通用的虛擬型號(hào)甚至出現(xiàn)“客戶機(jī)操作系統(tǒng)已禁用 CPU請(qǐng)關(guān)閉或重置虛擬機(jī)”的錯(cuò)誤這通常是虛擬化設(shè)置問(wèn)題不是 CPU 硬件損壞。6.2 實(shí)時(shí)監(jiān)控 CPU 占用率與溫度程序性能出問(wèn)題時(shí)CPU 占用率和溫度是最直接的兩個(gè)觀測(cè)指標(biāo)。Linux 下可以用 top 或 htop 看進(jìn)程 CPU 占用。更細(xì)粒度的可以用pidstat -p pid 1它能按進(jìn)程每秒鐘輸出 CPU 使用情況非常適合排查某個(gè)線程是否在持續(xù)吃滿單核。CPU 溫度在 Linux 下可以用 sensors 命令查看sensorsWindows 下可以用 HWiNFO 或 AIDA64。服務(wù)器上則通常依賴 BMC/IPMI 來(lái)讀取 CPU 溫度和功耗比如ipmitool sensor還有一個(gè)高頻問(wèn)題是 accounts-daemon 占用 CPU 很高這通常和桌面系統(tǒng)賬戶管理服務(wù)有關(guān)常見(jiàn)誘因是系統(tǒng)在后臺(tái)同步用戶信息或用戶目錄權(quán)限異常。排查方式一般是先確認(rèn)是哪個(gè)進(jìn)程在占用 CPU再根據(jù)進(jìn)程名定位對(duì)應(yīng)的服務(wù)而不是盲目 top 之后直接 kill 進(jìn)程。之前有用戶發(fā)現(xiàn) CTF 加載程序占用 CPU 過(guò)高后來(lái)定位到是輸入法相關(guān)組件的問(wèn)題。此類問(wèn)題通常需要從應(yīng)用層調(diào)優(yōu)與驅(qū)動(dòng)層面去解決不要輕易對(duì)系統(tǒng)進(jìn)程做強(qiáng)制終止操作。6.3 容器與虛擬化場(chǎng)景中的 CPU 限制現(xiàn)代應(yīng)用大量跑在容器和虛擬化環(huán)境里這就涉及 CPU 配額問(wèn)題。Kubernetes 中給 Pod 設(shè)置 CPU request 和 limit 后如果使用 CPU 節(jié)流throttling機(jī)制則當(dāng) Pod 使用超過(guò) limit 時(shí)會(huì)觸發(fā) CPU 限流。排查 K8s CPU 限流問(wèn)題時(shí)可以看容器 cgroup 的 cpu.statcat /sys/fs/cgroup/cpu/cpu.statnr_periods 1000 nr_throttled 300 throttled_time 123456789如果 nr_throttled 比例很高說(shuō)明容器經(jīng)常被打到 limit 上限。這時(shí)需要評(píng)估是提高 limit還是優(yōu)化應(yīng)用本身的 CPU 使用模式而不是盲目加副本。CPU 壓力測(cè)試可以借助 stress-ng 等工具但測(cè)試前要先確認(rèn)自己對(duì)生產(chǎn)環(huán)境的操作權(quán)限和可能影響。6.4 CPU 設(shè)計(jì)學(xué)習(xí)與仿真的啟發(fā)最后聊一個(gè)不少讀者感興趣的點(diǎn)很多人看到“O3”“Xring”這樣的詞會(huì)想自己去學(xué) CPU 設(shè)計(jì)。網(wǎng)上也能看到 “RISC-V 單周期 CPU 實(shí)驗(yàn)”、“CPU 設(shè)計(jì)實(shí)戰(zhàn) Lab” 這類課程。這類實(shí)驗(yàn)通常會(huì)讓你在 Verilog/SystemVerilog 里實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的單周期 RISC-V 處理器然后在 FPGA 上跑通指令集。單周期 CPU 是入門(mén)級(jí)的每條指令在一個(gè)時(shí)鐘周期內(nèi)完成設(shè)計(jì)很簡(jiǎn)單但主頻上不去。再往上做就是多周期流水線然后是經(jīng)典五級(jí)流水線再到亂序執(zhí)行。從“單周期”到“亂序”之間難度跨度非常大需要補(bǔ)充大量計(jì)算機(jī)體系結(jié)構(gòu)知識(shí)。如果你是剛?cè)腴T(mén)建議先跑通一個(gè)單周期 RISC-V 核再逐步往流水線和中斷設(shè)計(jì)擴(kuò)展。這個(gè)過(guò)程能讓你更直觀地理解 O3 核心的復(fù)雜來(lái)源也有助于你以后真正讀懂 CPU 評(píng)測(cè)里的 IPC、流水線深度、分支預(yù)測(cè)器等術(shù)語(yǔ)。6.5 手機(jī)端與移動(dòng)設(shè)備的 CPU 調(diào)度問(wèn)題手機(jī)上的多核 CPU 還有一大特點(diǎn)就是大小核架構(gòu)比如 ARM 的 big.LITTLE 或 DynamIQ。不同核的主頻和微架構(gòu)不同操作系統(tǒng)在調(diào)度任務(wù)時(shí)需要根據(jù)任務(wù)類型分配到合適的核上。日常簡(jiǎn)單操作放在小核重負(fù)載任務(wù)切到大核這就是“CPU 智能核心調(diào)度”要做的事。這也是為什么手機(jī) CPU 評(píng)測(cè)不能只看“多核分?jǐn)?shù)高低”。如果調(diào)度策略太激進(jìn)很快就把大核用起來(lái)性能分是高了但功耗和發(fā)熱也隨之上升。如果調(diào)度策略太保守則應(yīng)用啟動(dòng)慢、幀率波動(dòng)大。蘋(píng)果、高通、聯(lián)發(fā)科在這些方面的取舍各不相同芯片“能不能打”只是一個(gè)維度系統(tǒng)能否把芯片能力發(fā)揮出來(lái)同樣重要。7. 常見(jiàn)問(wèn)題與排查思路整理一下日常開(kāi)發(fā)中最常見(jiàn)到的 CPU 相關(guān)問(wèn)題列出排查思路問(wèn)題現(xiàn)象常見(jiàn)原因解決思路單線程程序性能不符合預(yù)期主頻和 IPC 之間不匹配代碼訪問(wèn)局部性差確認(rèn)程序是否受內(nèi)存延遲限制嘗試提升緩存命中率多核 CPU 跑多線程提升不明顯應(yīng)用并行度不足鎖競(jìng)爭(zhēng)嚴(yán)重緩存一致性開(kāi)銷大分析線程調(diào)度熱點(diǎn)減少共享數(shù)據(jù)寫(xiě)操作避免偽共享進(jìn)程 CPU 占用率長(zhǎng)期 100%可能是死循環(huán)、忙等待或日志風(fēng)暴用 top/pidstat 定位線程結(jié)合 jstack/perf 分析熱點(diǎn)容器 CPU 被限流應(yīng)用變慢K8s limit 設(shè)置過(guò)低或突發(fā)流量沖擊查看 cpu.stat評(píng)估 limit 與副本數(shù)必要時(shí)優(yōu)化代碼系統(tǒng)服務(wù) CPU 占用異常高部分后臺(tái)服務(wù)或驅(qū)動(dòng)異常定位具體進(jìn)程檢查日志更新驅(qū)動(dòng)謹(jǐn)慎再停用系統(tǒng)服務(wù)CPU 溫度過(guò)高觸發(fā)降頻散熱不足、硅脂老化、機(jī)箱風(fēng)道不合理或負(fù)載過(guò)重先測(cè)溫和記錄負(fù)載確認(rèn)是否為持續(xù)高負(fù)載再檢查散熱虛擬機(jī)報(bào) CPU 錯(cuò)誤BIOS 虛擬化未開(kāi)啟或虛擬機(jī)配置不對(duì)檢查虛擬化開(kāi)關(guān)確認(rèn)虛擬 CPU 類型與宿主機(jī)兼容這些問(wèn)題的共性是不要一上來(lái)就猜測(cè)是 CPU 性能不足而要先用監(jiān)控工具拿到數(shù)據(jù)再做橫向?qū)Ρ取?. 總結(jié)如何看待“單線程媲美 X、多線程大幅領(lǐng)先”這類信息把前面說(shuō)的內(nèi)容匯總一下。第一單線程性能的關(guān)鍵在于微架構(gòu)不是主頻數(shù)字。O3 亂序執(zhí)行、分支預(yù)測(cè)、緩存子系統(tǒng)共同決定了 IPC這是“媲美蘋(píng)果”這類表述真正要關(guān)注的點(diǎn)。真正可信的驗(yàn)證需要 SPEC CPU 等標(biāo)準(zhǔn)測(cè)試和實(shí)際應(yīng)用負(fù)載而不是網(wǎng)上流傳的參數(shù)表。第二多線程性能是一個(gè)系統(tǒng)能力不只由核心數(shù)決定。核心間的環(huán)形互聯(lián)、緩存一致性協(xié)議、內(nèi)存帶寬、調(diào)度器策略以及你的應(yīng)用是否高并行度都直接影響最終吞吐。Xring 這種名字如果最后被證實(shí)是某種片上互聯(lián)結(jié)構(gòu)那么它背后的設(shè)計(jì)取舍才是真正值得工程師研究的方向。第三評(píng)測(cè)數(shù)據(jù)要結(jié)合場(chǎng)景解讀。CPU 天梯圖上的分?jǐn)?shù)也好網(wǎng)上爆料的成績(jī)也好都是在特定條件、特定測(cè)試版本下獲得的。你需要明確自己的典型負(fù)載類型然后選擇合適的指標(biāo)來(lái)做參考。不要只看一個(gè)總分就下結(jié)論。第四無(wú)論新 CPU 最終表現(xiàn)如何現(xiàn)階段最好的學(xué)習(xí)姿勢(shì)是把“單線程、多線程、亂序執(zhí)行、緩存一致性、智能調(diào)度”這些基礎(chǔ)概念真正吃透。這樣以后看到任何一顆新架構(gòu)芯片的發(fā)布你都能快速看懂它的亮點(diǎn)在哪里、短板又可能出現(xiàn)在什么地方。如果你還想繼續(xù)深入學(xué)習(xí)建議按這個(gè)順序走先弄懂指令流水線再學(xué)亂序執(zhí)行的基本機(jī)制然后去看真實(shí)處理器的微架構(gòu)分析文章最后嘗試用 perf 或調(diào)優(yōu)工具觀察你自己程序的 CPU 行為。這個(gè)過(guò)程比收藏一堆“CPU 天梯圖”要有用得多。好今天的分享就到這里。如果你覺(jué)得這篇文章對(duì)你有幫助可以收藏備用。后續(xù)等官方發(fā)布更多細(xì)節(jié)我也會(huì)繼續(xù)寫(xiě)分析文章幫大家把架構(gòu)變化和實(shí)際開(kāi)發(fā)的關(guān)系講清楚。