五月份買了12代Alder Lake架構(gòu)的筆記本,CPU型號(hào)是i7 1280p,6大8小的配置。最近終于有機(jī)會(huì)研究一下這個(gè)架構(gòu)的特性,順便記錄一下感想。為了適配這個(gè)新的架構(gòu),我大幅修改了cpufp這個(gè)程序,大家可以幫忙多測(cè)一測(cè):
Alder Lake大小核架構(gòu)與拓?fù)?/h2>
在ubuntu 22.10下運(yùn)行l(wèi)stopo(可以通過apt install hwloc的方式獲得),可以看到如下處理器拓?fù)浣Y(jié)構(gòu):

可以看到:
[1] 處理器6個(gè)大核心,以及8個(gè)小核心都識(shí)別到了。編號(hào)上,大核心在前,小核心在后,這個(gè)特性和arm在android下是相反的。
[2] 每個(gè)大核心有獨(dú)立的L2,共享L3,且有兩個(gè)超線程。同一個(gè)核心的超線程優(yōu)先編號(hào),與早期Linux相反。
[3] 每個(gè)小核心只有一個(gè)線程。每四個(gè)小核心共享L2 cache。
這樣的架構(gòu)拓?fù)?,與以前的CPU拓?fù)溆辛撕艽蟛顒e。以前的架構(gòu),全部是同構(gòu)核心,即便有NUMA這樣非對(duì)稱內(nèi)存訪問結(jié)構(gòu),也還算簡(jiǎn)單,并行程序在分配,管理,訪問內(nèi)存的時(shí)候注意區(qū)分本地內(nèi)存和遠(yuǎn)端內(nèi)存即可,在任務(wù)調(diào)度上不需要考慮因同樣的任務(wù)在不同核心上計(jì)算效能的差異導(dǎo)致的負(fù)載不均衡問題。
所以舊版本的cpufp做多線程并行的方式,問題就很大了:假設(shè)所有核心都是同構(gòu)核心,靜態(tài)任務(wù)分配,會(huì)造成多核心測(cè)試負(fù)載不均衡;大小核超線程的數(shù)量的不同,以及超線程的新的編號(hào)方式,導(dǎo)致線程池的親和性設(shè)置很難自動(dòng)且正確地完成?;谶@些問題,我修改了cpufp的綁核方式,需要在命令行參數(shù)由用戶明確指定綁定線程的編號(hào)(圖中的PU編號(hào))。比如要開6個(gè)線程的線程池,綁定6個(gè)大核,那么需要運(yùn)行:
./cpufp --thread_pool=[0,2,4,6,8,10]
要綁定小核,則參數(shù)可以用“-”號(hào)表示連續(xù)區(qū)間:
./cpufp --thread_pool=[12-19]
這樣并沒有解決自動(dòng)負(fù)載均衡的問題,但是對(duì)于cpufp這個(gè)demo程序也足夠用了。
新指令集AVX VNNI
Alder Lake架構(gòu)雖然屏蔽掉了AVX512系列指令集,但是仍然保留了AVX VNNI指令集,可以把它看成是AVX512 VNNI指令集的256和128位子集。但是,它是一個(gè)全新的指令集,在編碼上與AVX512并不相同,指令需要加上{vex}前綴,否則生成的機(jī)器碼是AVX512 VNNI的,在不支持AVX512 VNNI的CPU上會(huì)報(bào)illegal instruction,{vex}前綴表示編譯成AVX版本的指令。下面一段代碼是測(cè)試AVX VNNI的int8峰值的代碼:
cpufp_kernel_x86_avx_vnni_int8:
vpxor %ymm0, %ymm0, %ymm0
vpxor %ymm1, %ymm1, %ymm1
vpxor %ymm2, %ymm2, %ymm2
vpxor %ymm3, %ymm3, %ymm3
vpxor %ymm4, %ymm4, %ymm4
vpxor %ymm5, %ymm5, %ymm5
vpxor %ymm6, %ymm6, %ymm6
vpxor %ymm7, %ymm7, %ymm7
vpxor %ymm8, %ymm8, %ymm8
vpxor %ymm9, %ymm9, %ymm9
.cpufp.x86.avx.vnni.int8.L1:
{vex} vpdpbusd %ymm0, %ymm0, %ymm0
{vex} vpdpbusd %ymm1, %ymm1, %ymm1
{vex} vpdpbusd %ymm2, %ymm2, %ymm2
{vex} vpdpbusd %ymm3, %ymm3, %ymm3
{vex} vpdpbusd %ymm4, %ymm4, %ymm4
{vex} vpdpbusd %ymm5, %ymm5, %ymm5
{vex} vpdpbusd %ymm6, %ymm6, %ymm6
{vex} vpdpbusd %ymm7, %ymm7, %ymm7
{vex} vpdpbusd %ymm8, %ymm8, %ymm8
{vex} vpdpbusd %ymm9, %ymm9, %ymm9
sub $0x1, %rdi
jne .cpufp.x86.avx.vnni.int8.L1
ret
VNNI指令集支持int8和int16兩種精度,現(xiàn)在都已加入cpufp的benchmark里。同時(shí)新版本的cpufp可以在編譯期(執(zhí)行build.sh時(shí))識(shí)別本機(jī)支持的指令集,直接生成支持指令集的benchmark測(cè)試,避免了舊版系統(tǒng)編譯不了新指令集的問題。
Alder Lake峰值測(cè)試結(jié)果與分析
修改后的cpufp代碼在我的i7-1280P上測(cè)試結(jié)果如下:
$ ./cpufp --thread_pool=[0]
Number Threads: 1
Thread Pool Binding: 0
--------------------------------------------------
| Instruction Set | Data Type | Peak Performance |
| AVX_VNNI | INT8 | 590.31 GOPS |
| AVX_VNNI | INT16 | 295.06 GOPS |
| FMA | FP32 | 149.87 GFLOPS |
| FMA | FP64 | 74.931 GFLOPS |
| AVX | FP32 | 112.39 GFLOPS |
| AVX | FP64 | 56.203 GFLOPS |
| SSE | FP32 | 56.054 GFLOPS |
| SSE | FP64 | 28.001 GFLOPS |
--------------------------------------------------
$ ./cpufp --thread_pool=[0,2,4,6,8,10]
Number Threads: 6
Thread Pool Binding: 0 2 4 6 8 10
--------------------------------------------------
| Instruction Set | Data Type | Peak Performance |
| AVX_VNNI | INT8 | 2636.8 GOPS |
| AVX_VNNI | INT16 | 1319.1 GOPS |
| FMA | FP32 | 670.05 GFLOPS |
| FMA | FP64 | 335 GFLOPS |
| AVX | FP32 | 502.4 GFLOPS |
| AVX | FP64 | 251.2 GFLOPS |
| SSE | FP32 | 250.42 GFLOPS |
| SSE | FP64 | 125.16 GFLOPS |
--------------------------------------------------
$ ./cpufp --thread_pool=[12]
Number Threads: 1
Thread Pool Binding: 12
--------------------------------------------------
| Instruction Set | Data Type | Peak Performance |
| AVX_VNNI | INT8 | 114.89 GOPS |
| AVX_VNNI | INT16 | 57.445 GOPS |
| FMA | FP32 | 57.444 GFLOPS |
| FMA | FP64 | 28.723 GFLOPS |
| AVX | FP32 | 28.723 GFLOPS |
| AVX | FP64 | 14.362 GFLOPS |
| SSE | FP32 | 28.312 GFLOPS |
| SSE | FP64 | 14.361 GFLOPS |
--------------------------------------------------
$ ./cpufp --thread_pool=[12-19]
Number Threads: 8
Thread Pool Binding: 12 13 14 15 16 17 18 19
--------------------------------------------------
| Instruction Set | Data Type | Peak Performance |
| AVX_VNNI | INT8 | 867.99 GOPS |
| AVX_VNNI | INT16 | 434 GOPS |
| FMA | FP32 | 434 GFLOPS |
| FMA | FP64 | 217 GFLOPS |
| AVX | FP32 | 217.01 GFLOPS |
| AVX | FP64 | 108.5 GFLOPS |
| SSE | FP32 | 216.39 GFLOPS |
| SSE | FP64 | 108.5 GFLOPS |
--------------------------------------------------
第一個(gè)表格是單個(gè)大核的執(zhí)行結(jié)果,可以看到如下特性:
[1] AVX VNNI指令集的int8吞吐,是FMA指令集(CPU最大浮點(diǎn)吞吐指令)中fp32的4倍;int16則是fp32的2倍,與其他支持dp4a類指令架構(gòu)非常一致。
[2] SSE指令是AVX指令對(duì)應(yīng)精度類型的正好吞吐,這個(gè)與以往在intel先前架構(gòu)也吻合。
[3] AVX指令和FMA指令對(duì)比,對(duì)應(yīng)精度類型的吞吐大約是(
)。這就與Intel前面幾代架構(gòu)有了較大的差別。我在另外一臺(tái)10代Comet Lake架構(gòu)CPU(桌面Skylake架構(gòu)的某改ya良gao版)上測(cè)試的結(jié)果如下:
$ ./cpufp --thread_pool=[0]
Number Threads: 1
Thread Pool Binding: 0
--------------------------------------------------
| Instruction Set | Data Type | Peak Performance |
| FMA | FP32 | 125.93 GFLOPS |
| FMA | FP64 | 62.898 GFLOPS |
| AVX | FP32 | 62.948 GFLOPS |
| AVX | FP64 | 31.491 GFLOPS |
| SSE | FP32 | 31.28 GFLOPS |
| SSE | FP64 | 15.686 GFLOPS |
--------------------------------------------------
可以看出該架構(gòu)中AVX指令對(duì)應(yīng)浮點(diǎn)類型的吞吐是FMA指令的一半。這是由于,Alder Lake之前的架構(gòu),浮點(diǎn)向量乘加類指令集中在port0和port1這兩個(gè)發(fā)射端口(port5作為AVX512唯一完整的發(fā)射端口,經(jīng)常在桌面架構(gòu)或者低端服務(wù)器產(chǎn)品屏蔽掉浮點(diǎn)乘加),這兩個(gè)端口一般各有一條256位的FMA單元。同時(shí),這兩個(gè)端口也支持256位的MUL和ADD指令,或者,其中一個(gè)端口支持MUL,另一個(gè)端口支持ADD。這樣我們?cè)跍y(cè)試AVX指令時(shí)用到MUL和ADD,分別只有FMA指令一半的吞吐(乘和加各算一次計(jì)算,所以乘加相比乘或者加單獨(dú)的指令就是兩倍的浮點(diǎn)吞吐)。然后我們?cè)倏匆幌翧lder Lake大核Golden Cove的架構(gòu):

我們發(fā)現(xiàn)Port5也多了一條FastADD單元(Fast指的是延遲周期更短)。這樣,Golden Cove在Port0和Port1各有一條256位的FMA;在Port0和Port1各有一條MUL(與FMA單元共享);同時(shí),Port1和Port5各有一條FastADD。因此Port1既可以發(fā)射MUL,也可以發(fā)射FastADD。我們的AVX指令測(cè)試程序是下面這樣乘加交換排指令流水的:
.cpufp.x86.avx.fp32.L1:
vmulps %ymm12, %ymm12, %ymm0
vaddps %ymm12, %ymm12, %ymm1
vmulps %ymm12, %ymm12, %ymm2
vaddps %ymm12, %ymm12, %ymm3
vmulps %ymm12, %ymm12, %ymm4
vaddps %ymm12, %ymm12, %ymm5
vmulps %ymm12, %ymm12, %ymm6
vaddps %ymm12, %ymm12, %ymm7
vmulps %ymm12, %ymm12, %ymm8
vaddps %ymm12, %ymm12, %ymm9
vmulps %ymm12, %ymm12, %ymm10
vaddps %ymm12, %ymm12, %ymm11
sub $0x1, %rdi
jne .cpufp.x86.avx.fp32.L1
執(zhí)行的時(shí)候,第一個(gè)周期發(fā)射乘加乘,第二個(gè)周期發(fā)射加乘加,第三個(gè)周期又是發(fā)射乘加乘... 以此類推。三個(gè)端口支持乘和加1:2和2:1兩種比例,都可以填滿流水線。這樣浮點(diǎn)吞吐量正好是兩條FMA流水線的,算是近幾代intel架構(gòu)里一個(gè)不小的改進(jìn),為AVX和SSE(SSE就是簡(jiǎn)單地復(fù)用AVX的一半計(jì)算單元)優(yōu)化的重型浮點(diǎn)程序,可以在Golden Cove上獲得相當(dāng)?shù)男阅芴嵘↖PC提升)。
第二個(gè)表格,是6個(gè)大核的測(cè)試,由于低功耗版處理器的限制,多核頻率達(dá)不到單核的6倍,所以整體計(jì)算吞吐沒有達(dá)到6倍,表現(xiàn)算是正常。
第三個(gè)表格是小核心單核,可以看出如下幾個(gè)特性:
[1] AVX VNNI的int8吞吐只有FMA的fp32吞吐的2倍,int16與FP32的吞吐持平,小核的AI能力縮水不少。
[2] 小核心的浮點(diǎn)吞吐只有大核心的多一點(diǎn),一方面因?yàn)樾『诵闹挥幸粭lFMA流水線,另一方面是頻率也有差距。即便使小核心對(duì)比10代Comet Lake處理器,也只有不到
的吞吐(頻率差距小一些)。
[3] 小核的AVX指令吞吐只有FMA的一半,與之前的架構(gòu)一致,比起大核的差距拉的更大了(接近)。
再加上Cache容量和架構(gòu)上的精簡(jiǎn),小核實(shí)現(xiàn)同樣計(jì)算的效率肯定是不如大核的。所謂小核接近skylake的說法,至少在浮點(diǎn)或向量密集型生產(chǎn)力應(yīng)用上,小核就是雞肋,幫不上什么忙。
第四個(gè)表格小核多核吞吐與大核類似,同樣也達(dá)不到8倍。
CPU并行編程的新挑戰(zhàn)
其實(shí)這個(gè)挑戰(zhàn)從移動(dòng)端arm架構(gòu)引入bigLittle就開始了,但終究與桌面和服務(wù)器端有所不同。移動(dòng)端SoC發(fā)展到今天,高通已經(jīng)搞出單個(gè)SoC里,實(shí)現(xiàn)1 + 2 + 2 + 3四種異構(gòu)核心。一個(gè)大核的能力非常強(qiáng),中間兩種中核比較接近,比大核慢到一半左右,小核性能極差,基本只是為了跑一些常規(guī)應(yīng)用時(shí)降低功耗。這樣的系統(tǒng)環(huán)境,再加上由于電池環(huán)境導(dǎo)致的小核優(yōu)先調(diào)度策略,使并行編程難如登天。我們?yōu)槭謾C(jī)和APP業(yè)務(wù)開發(fā)計(jì)算密集型應(yīng)用,在使用多核編程時(shí)候問題非常多,要么降頻,要么優(yōu)先調(diào)度小核,且沒法控制,甚至很多時(shí)候大小核需要不同的代碼來(lái)達(dá)到最優(yōu)性能,根本無(wú)法兼顧,所以很多時(shí)候只是用單個(gè)大核在跑應(yīng)用,減少?gòu)?fù)雜性和混沌。還好隨著GPU OpenCL/Vulkan環(huán)境,以及SoC里面的DSP和AI加速器日趨成熟,我們很多移動(dòng)端的項(xiàng)目已經(jīng)大量遷移到這些更高效能的處理器中。但是桌面和云端環(huán)境,我們終究是希望利用多個(gè)CPU核心以提升總的效率。尤其是云端還面臨核數(shù)眾多,NUMA非對(duì)稱內(nèi)存訪問等問題。
并行計(jì)算在劃分任務(wù)的時(shí)候,通常分為靜態(tài)劃分和動(dòng)態(tài)劃分,目標(biāo)都是為了給不同計(jì)算核心分配均勻的負(fù)載,以追求線性加速。靜態(tài),是按照可并行的線程單元數(shù)量,平均劃分任務(wù),且在運(yùn)行時(shí)不能改變?nèi)蝿?wù)劃分方式。動(dòng)態(tài)則是在運(yùn)行時(shí),根據(jù)各個(gè)線程的狀態(tài),動(dòng)態(tài)分配任務(wù),使多個(gè)線程動(dòng)態(tài)調(diào)整自己的負(fù)載,大致達(dá)到均衡狀態(tài)。前者的好處是方便開發(fā),單個(gè)線程執(zhí)行效率最高,對(duì)大多數(shù)并行度很好的程序有非常好的并行效果。后者的好處是可以根據(jù)運(yùn)行時(shí)的各種突發(fā)問題做出調(diào)整,防止因條件變化導(dǎo)致任務(wù)負(fù)載不均。下面這個(gè)圖展示了這兩種調(diào)度方式在同構(gòu)核心和異構(gòu)大小核環(huán)境下的執(zhí)行模式:

左邊展示了同構(gòu)大核使用靜態(tài)調(diào)度的方式,一般可以取得不錯(cuò)的負(fù)載均衡。中間這個(gè)圖換成2大2小的核心,對(duì)于小核心,同樣的任務(wù)執(zhí)行時(shí)間變長(zhǎng),導(dǎo)致靜態(tài)調(diào)度后,負(fù)載不均衡。大小核對(duì)于不同任務(wù)的執(zhí)行速度比例也可能不一樣(比如大小核分別計(jì)算FFT,是2:1的吞吐;計(jì)算矩陣乘法可能就變成4:1。甚至同一種計(jì)算使用不同參數(shù),這個(gè)比例也會(huì)改變),很難根據(jù)不同架構(gòu)來(lái)分配不同大小的靜態(tài)任務(wù)。唯一的辦法,就如最后一張圖所示,縮小單個(gè)任務(wù)的規(guī)模,增加任務(wù)數(shù)量,并進(jìn)行動(dòng)態(tài)調(diào)度,這樣不同的處理器核可以根據(jù)自己的“胃口”,吃進(jìn)適合自己的任務(wù)量,基本達(dá)到平衡。
過去的CPU都是同構(gòu)核心,對(duì)于已有的,可以并行的軟件和代碼,為了開發(fā)簡(jiǎn)便,相當(dāng)一部分并行的工作都是靜態(tài)任務(wù)劃分,比如簡(jiǎn)單調(diào)用OpenMP的循環(huán)并行。這樣很容易造成大小核同時(shí)加速這個(gè)程序的時(shí)候,并行效果并不好。
同時(shí),這種大量小任務(wù)動(dòng)態(tài)調(diào)度的方式,還有一些問題:
對(duì)于這種調(diào)度方式,任務(wù)體量越小,數(shù)量越多,越容易達(dá)到負(fù)載均衡,均衡誤差也越小。但是很多并行任務(wù)的并行度是有限的,可以拆成更細(xì)粒度的小任務(wù)是有上限的,天然限制了這個(gè)方案;同時(shí)每個(gè)任務(wù)有相對(duì)固定的拆分成本和調(diào)度成本,任務(wù)越多,拆分和調(diào)度的開銷占比就越大。
另外,對(duì)于優(yōu)化好流水線的單個(gè)計(jì)算任務(wù)來(lái)講,如果拆成更小的任務(wù),那么就會(huì)多出很多進(jìn)出流水線的開銷,如下圖所示:

所以總的來(lái)說,靜態(tài)任務(wù)改成大量小任務(wù)動(dòng)態(tài)劃分和調(diào)度的方式,效果有其極限,隨著任務(wù)拆分越多,延遲變化呈U字形變化,先降低后升高。
結(jié)語(yǔ)
今后的CPU并行編程,我們要面對(duì)的困難更多了。除了要考慮核心越來(lái)越多,NUMA非對(duì)稱這些問題,還要關(guān)注intel和arm這些CPU大小核的異構(gòu)任務(wù)調(diào)度問題。操作系統(tǒng)對(duì)這些問題的解決能力十分有限,寄希望于新內(nèi)核對(duì)調(diào)度系統(tǒng)的改進(jìn),其實(shí)是緣木求魚,只要不添亂就好了。任何嚴(yán)肅的大規(guī)模系統(tǒng)軟件和生產(chǎn)力系統(tǒng),都必須自己解決資源的調(diào)度和優(yōu)化。Intel最開始引入大小核的目的,其實(shí)是想在單核和多核兩種場(chǎng)景都可以取得跑分的突破。但是忽略了對(duì)于現(xiàn)有大量遺留軟件的適配難度問題,以及開發(fā)新軟件帶來(lái)的成本和難度的挑戰(zhàn)。硬件性能的提升,還是盡量不要給軟件帶來(lái)太多負(fù)擔(dān)為好。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 恩,這個(gè)系列的第一篇文章,先談點(diǎn)輕松的,常用CPU架構(gòu)浮點(diǎn)峰值的理論計(jì)算和實(shí)測(cè)。做性能優(yōu)化,先要知己知彼,了解自己...
- 1.3 技術(shù)亮點(diǎn) 技術(shù)亮點(diǎn) AI數(shù)據(jù)存儲(chǔ)與計(jì)算,核心機(jī)器學(xué)習(xí)算法 - 面向100G + RoCE 設(shè)計(jì)的自研...
- 學(xué)了IT那么久,只知道CPU主頻不斷的提升,核數(shù)也越來(lái)越多,但是如何理論上計(jì)算出不同代CPU性能的提升,還真...
- 本文翻譯自: https://www.anandtech.com/show/14385/arm-announces...
- 微信公眾號(hào):云計(jì)算通俗講義 持續(xù)輸出技術(shù)干貨,歡迎關(guān)注! 通過本文你將了解: 架構(gòu) 性能指標(biāo) 監(jiān)控工具 故障分析 ...