因?yàn)橐粋€循環(huán),CPU搞了個新技術(shù)!
好久不見,我叫阿Q,是CPU一號車間的員工。我所在的CPU有8個車間,也就是8個核心,咱們每個核心都可以同時執(zhí)行兩個線程,就是8核16線程,那速度杠杠滴。

我所在的一號車間,除了負(fù)責(zé)執(zhí)行指令的我,還有負(fù)責(zé)讀取指令的小A,負(fù)責(zé)指令譯碼的小胖和負(fù)責(zé)結(jié)果回寫的老K,我們幾個各司其職,一起完成執(zhí)行程序的工作。
一個簡單的循環(huán)
那天,我們遇到了一段代碼:
void array_add(int data[], int len) {
for (int i = 0; i < len; i++) {
data[i] += 1;
}
}
循環(huán)了好幾百次之后,才把這段代碼執(zhí)行完成,每次循環(huán)都是做簡單又重復(fù)的工作,把我累得夠嗆。
一旁負(fù)責(zé)結(jié)果回寫的老K也是累的滿頭大汗,吐槽道:“每次都是取出來加1又寫回去,要是能一次多取幾個數(shù),批量處理就好了”
老K的話讓我眼前一亮,對啊,能不能批量操作呢?
心里一邊想著,一邊繼續(xù)干活了。
繁忙的一天很快結(jié)束了,轉(zhuǎn)眼又到了晚上,計(jì)算機(jī)關(guān)機(jī)后,我把大家召集了起來。
“兄弟們,還記得咱們白天遇到的那個循環(huán)嗎?”
“你說哪個循環(huán),咱們這一天可執(zhí)行了不少循環(huán)呢”,小A說到。
“就是那個把整數(shù)數(shù)組每個元素都加1的那個循環(huán)”
“我想起來了,那循環(huán)怎么了?有什么問題嗎?”
我看了老K一眼,說道:“我在想今天老K的話,像這種循環(huán),每次都是取出來加1又寫回去,一次操作一個數(shù),效率太低了,咱們要是升級改造一下,支持一次取出多個數(shù),批量加1,這樣豈不是快很多?”

老K一聽來了興趣,“這敢情好,你打算怎么做?”
“這我還沒想好,大家有什么建議嗎?”
一旁負(fù)責(zé)指令譯碼的小胖說道:“可以新增一條指令,專門用來一次取出多個數(shù)據(jù)來加1”
“不行不行,不能限的這么死,今天是加1,萬一下次是加2呢?指令里面不能限制為1”
“那如果每個數(shù)據(jù)要加的是不一樣的怎么辦?”
“你這么一說,那萬一不是加法,是減法,乘法怎么辦?”
“還有啊,···”
大家開始七嘴八舌討論了起來,沒想到一個小小的加法循環(huán),一下子引出了這么多問題來,這是我們沒想到的。
并行計(jì)算
隨著討論的深入,我覺得已經(jīng)超出了咱們一號車間能把控的范圍,需要上報給領(lǐng)導(dǎo),組織八個車間代表一起來商討。
領(lǐng)導(dǎo)一聽說有提高性能的新技術(shù),馬上來了興趣,很快便開會組織大家一起來商討方案。

“都到齊了是吧,阿Q你給大家說一下這個會議的目的”,領(lǐng)導(dǎo)說到。
我站了起來,開始把我們遇到的問題和想法跟大家講了一遍。
“是這樣的,我們一號車間那天遇到了一段循環(huán)代碼,循環(huán)體的內(nèi)容很簡單,就是給數(shù)組中的每一個元素加1。我們執(zhí)行的時候,就是不斷取出每一個元素,然后將其執(zhí)行加法計(jì)算后,再寫回去。這樣一個一個來加1,我們感覺太慢了, 要是可以一次多取幾個,并行加1,那一定比一個一個加快上不少?!?/p>
我剛說完,大家都開始小聲議論起來。
“我看出來了,這其實(shí)就是并行計(jì)算!”,二號車間小虎一語道出了關(guān)鍵。
六號車間小六問道:”阿Q,你們已經(jīng)有方案了嗎?“
“還沒有,這正是今天開會的目的,因?yàn)榍闆r有點(diǎn)復(fù)雜,還需要大家一起來出出主意”
“好像并不復(fù)雜嘛”
“我上面舉的例子只是一個簡單的情況,并行計(jì)算還可能不是固定的數(shù),可能是一個數(shù)組和另一個數(shù)組相加。還有可能不是整數(shù)相加,而是浮點(diǎn)數(shù),甚至,還可能不是加法,而是減法或者乘法,再或者不是算術(shù)運(yùn)算,而是邏輯運(yùn)算”
我剛一說完,大家又開始竊竊私語交流起來。
“我琢磨著你說的這一系列東西,咱們是要新增一套專門用來并行計(jì)算的指令集啊”,小虎說道。
“這可是大工程啊”
“是啊···”
這時,小六又問道:“咱們的計(jì)算的時候,都是把數(shù)據(jù)讀取到寄存器進(jìn)行的,可這寄存器一次只能裝一個數(shù),怎么一次讀取多個數(shù)據(jù)呢?”
“可能需要新增一些容量大一些的寄存器,比如128bit長度,可以同時容納4個32位的整數(shù)”

“有這個必要嗎?咱們是通用CPU,又不是專門做數(shù)學(xué)計(jì)算的芯片,搞這些東西干嘛?”,四號車間代表提出了質(zhì)疑。
我也不甘示弱:“那可太有必要了,在圖像、視頻、音頻處理等領(lǐng)域,有大量這樣的計(jì)算需求,咱們得提升處理這些數(shù)據(jù)的能力”
見我們爭執(zhí)不下,領(lǐng)導(dǎo)拍了拍桌子,會場一下安靜了下來。
“我覺得阿Q說的有道理,咱們確實(shí)需要提升處理這類數(shù)據(jù)運(yùn)算的能力了。不過不用一下搞那么復(fù)雜,先支持整數(shù)并行運(yùn)算就行了。新增寄存器這個也不用著急,可以先借用一下浮點(diǎn)數(shù)運(yùn)算單元FPU的寄存器。這件事先這么定下來,具體的方案你們再繼續(xù)討論?!?,說完便離開了會議室。
領(lǐng)導(dǎo)不愧是領(lǐng)導(dǎo),幾句話就把我們安排的明明白白。
SIMD
又經(jīng)過一陣緊張的討論,我們終于敲定了方案。
我們借用浮點(diǎn)數(shù)運(yùn)算單元的寄存器,還給它們起了新的名字:MM0-MM7。因?yàn)槭?4位的寄存器,所以可以同時存儲兩個32位的整數(shù)或者4個16位整數(shù)或者8個8位的整數(shù)。
我們還新增了一套叫MMX的指令集,用來并行執(zhí)行整數(shù)的運(yùn)算。

我們把這種在一條指令中同時處理多個數(shù)據(jù)的技術(shù)叫做單指令多數(shù)據(jù)流(Single Instruction Multiple Data),簡稱SIMD。
有了這套指令集,咱們處理這類整數(shù)運(yùn)算問題的速度快了不少。
不過漸漸地發(fā)現(xiàn)了兩個很麻煩的問題:
- 第一個問題,因?yàn)槭墙栌肍PU的寄存器,所以當(dāng)執(zhí)行SIMD指令的時候,就不能用FPU計(jì)算單元,反過來也一樣,同時使用的話就會出亂子,所以要經(jīng)常在不同的模式之間切換,實(shí)在是有些麻煩。
- 另一個更重要的問題,咱們這套指令集只能處理整數(shù)的并行運(yùn)算,可現(xiàn)在浮點(diǎn)數(shù)的并行運(yùn)算越來越多,尤其是圖像、視頻還有深度學(xué)習(xí)的一些數(shù)據(jù)處理,浮點(diǎn)數(shù)情況越來越多,這時候都派不上用場。
我們把這些問題給領(lǐng)導(dǎo)做了匯報,看到我們已經(jīng)做出的成績,領(lǐng)導(dǎo)終于同意繼續(xù)升級。
這一次,我們擴(kuò)展了一套新的SSE指令集出來,新增了XMM0-XMM7總共8個128位的寄存器,再也不用跟FPU共享寄存器了。而且位寬加了一倍,能容納的數(shù)據(jù)更多了,能同時處理的數(shù)據(jù)自然也變多了。
后來,我們又不斷的修改升級,不僅支持了對浮點(diǎn)數(shù)并行處理,還推出了新一代的AVX指令集,把寄存器再一次擴(kuò)大為256位,現(xiàn)在我們的SIMD技術(shù)更加先進(jìn),處理數(shù)據(jù)運(yùn)算的能力越來越強(qiáng)了!




























