您好,登錄后才能下訂單哦!
這篇文章主要講解了“Linux高性能任務(wù)獨(dú)占CPU舉例分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Linux高性能任務(wù)獨(dú)占CPU舉例分析”吧!
Part 1工程需求
在一個SMP或者NUMA系統(tǒng)中,CPU的數(shù)量大于1。在工程中,我們有時候有一種需求,就是讓某個能夠獨(dú)占CPU,這個CPU什么都不做,就只做指定的任務(wù),從而獲得低延遲、高實(shí)時的好處。
比如在DPDK中,通過設(shè)置
GRUB_CMDLINE_LINUX_DEFAULT=“isolcpus=0-3,5,7”
隔離CPU0,3,5,7,讓DPDK的任務(wù)在運(yùn)行的時候,其他任務(wù)不會和DPDK的任務(wù)進(jìn)行上下文切換,從而保證網(wǎng)絡(luò)性能最佳[1]。在Realtime應(yīng)用場景中,通過isolcpus=2隔離CPU2,然后把實(shí)時應(yīng)用通過taskset綁定到隔離的核:
taskset-c 2 pn_dev
從而保證低延遲要求[2]。
Part 2用戶態(tài)隔離
這個地方,我們可以看出,它們統(tǒng)一都使用了isolcpus這樣一個啟動參數(shù)。
實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),下面我們來啟動一個8核的ARM64系統(tǒng),運(yùn)行Ubuntu,并指定isolcpus=2這個啟動參數(shù):
系統(tǒng)啟動后,我們運(yùn)行下面簡單的程序(啟動8個進(jìn)程運(yùn)行while死循環(huán)):
我們是8核的,現(xiàn)在又是運(yùn)行8個進(jìn)程,所以理論上來講,負(fù)載均衡后,8個進(jìn)程應(yīng)該均分地運(yùn)行在8個核上面,但是我們來看看實(shí)際的htop結(jié)果:
我們發(fā)現(xiàn)3(也就是CPU2)上面的CPU占用率是0.0%。這實(shí)證了CPU2已經(jīng)被隔離,用戶空間的進(jìn)程不能在它上面跑。
當(dāng)然,這個時候,我們可以通過taskset,強(qiáng)行把其中的一個a.out,綁定到CPU2上面去:
從上面命令的結(jié)果看出,663原本的affinity list只有0,1,3-7是沒有2的,而我們強(qiáng)行把它設(shè)置為了2,之后再看htop,CPU2上面占用100%:
通過上面的實(shí)驗(yàn),我們明顯可以看出isolcpus=2使得CPU2上無法再運(yùn)行用戶空間的進(jìn)程了(除非手動設(shè)置affinity)。
Part 3內(nèi)核態(tài)隔離
中斷
但是,能在CPU2上面運(yùn)行的,不是只有用戶態(tài)的任務(wù),還可以有內(nèi)核線程、中斷等,那么isolcpus=能否隔離內(nèi)核線程和中斷呢?
對于中斷,我們特別容易查看,就是實(shí)際去驗(yàn)證每個IRQ的smp_affinity就好了:
從上圖明顯可以看出,對于44、47號這種外設(shè)的中斷,Linux內(nèi)核把smp_affinity設(shè)置為了FB(11111011),明顯避開了CPU2,所以,實(shí)際外設(shè)中斷也不會在CPU2發(fā)生,除非我們強(qiáng)行給中斷綁核,比如讓44號中斷綁定到CPU2:
echo 2 >/proc/irq/44/smp_affinity_list
之后,我們發(fā)現(xiàn)44號中斷在CPU2可以發(fā)生:
但是,系統(tǒng)的timer中斷、IPI,由于是Linux系統(tǒng)的運(yùn)行基石,實(shí)際還是要在CPU2上面運(yùn)行的。這里面最可能給任務(wù)帶來延遲抖動的,自然是timer tick。
下面我們重點(diǎn)探討下tick的問題,由于Linux一般情況下,已經(jīng)配置IDLE狀態(tài)的NO_HZ tickless,所以CPU2上面什么都不跑的時候,實(shí)際timer中斷幾乎不發(fā)生。
下面,我們還是在isolcpus=2的情況下,運(yùn)行前面那個8個進(jìn)程的a.out,默認(rèn)情況下沒有任務(wù)會占用CPU2。通過先后運(yùn)行幾次cat /proc/interrupts | head 2,我們會看到其他core的timer中斷頻繁發(fā)生,而CPU2幾乎不變,這顯然是IDLE時候的NO_HZ在發(fā)揮省電的作用:
但是,一旦我們放任務(wù)到CPU2,哪怕只是放1個,就會發(fā)現(xiàn)CPU2上面的timer中斷開始增加:
這說明一點(diǎn),哪怕隔離的CPU上面只有一個線程去跑,timer tick就會開始跑,當(dāng)然,這個timer tick也會頻繁打斷這一個線程,從而造成大量的上下文切換。你肯定會覺得Linux怎么這么傻,既然只有一個人,那也沒有時間片分片的必要,不需要在2個或者多個任務(wù)進(jìn)行時間片劃分地調(diào)度,為啥還要跑tick?其實(shí)原因是我們的內(nèi)核默認(rèn)只是使能了IDLE的NO_HZ:
我們來重新編譯一個內(nèi)核,使能NO_HZ_FULL:
當(dāng)我們使能了NO_HZ_FULL后,Linux支持在CPU上僅有1個任務(wù)的時候,是可以NO_HZ的。但是有2個就傻眼了,所以這個“FULL”也不是真地FULL[3]。這當(dāng)然也可以理解,因?yàn)橛?個就涉及到時間片調(diào)度的問題。什么時候應(yīng)該使能NO_HZ_FULL,內(nèi)核文檔Documentation/timers/no_hz.rst有明確地“指示”,只有在實(shí)時和HPC等的場景,才需要,否則默認(rèn)的NO_HZ_IDLE是你最好的選擇:
我們重新編譯了內(nèi)核,選中了NO_HZ_FULL,下面啟動Linux,注意啟動的時候參數(shù)添加nohz_full=2,讓CPU2支持NO_HZ_FULL:
重新運(yùn)行CPU2只有一個任務(wù)的場景,看看它的timer中斷發(fā)生情況:
發(fā)現(xiàn)CPU2上面的tick穩(wěn)定在188上面,這樣相信你會更加開心,因?yàn)槟悛?dú)占地更加徹底了!
下面,我們再放一個task進(jìn)去CPU2,有2個任務(wù)的情況下,CPU2上面的timer tick開始增加:
不過,這或許不是個問題,因?yàn)槲覀冋f好了“獨(dú)占”,1個任務(wù)獨(dú)占的時候,timer tick不來打擾,應(yīng)該已經(jīng)是非常理想的情況了!
內(nèi)核態(tài)線程
內(nèi)核態(tài)的線程其實(shí)和用戶態(tài)差不多,當(dāng)它們沒有綁定到隔離的CPU的時候,是不會跑到隔離CPU運(yùn)行的。下面用筆者在內(nèi)核里面添加的dma_map_benchmark來做實(shí)驗(yàn)[4],開啟16個內(nèi)核線程來進(jìn)行DMA map和unmap(注意我們只有8個核):
./dma_map_benchmark -s 120 -t 16
我們看到CPU2上面的CPU占用也是0:
內(nèi)核里面的dma_map_benchmark線程在狂占CPU0-1, 3-7,但是就是不去占CPU2:
但是,內(nèi)核線程如果用kthread_bind_mask()類似API把線程綁定到了隔離的CPU,則情況就不一樣了,這就類似用taskset把用戶態(tài)的任務(wù)綁定到CPU一樣。
Part 4最佳實(shí)踐指南
對于實(shí)時性要求高、高性能計(jì)算等場景,如果要讓某個任務(wù)獨(dú)占CPU,最理想的選擇是:
1. 采用isolcpus隔離CPU
2. 將指定任務(wù)綁定到隔離CPU
3. 小心意外地把中斷、內(nèi)核線程綁定到了隔離CPU,排查到這些“意外”分子
4. 使能NO_HZ_FULL,則效果更佳,因?yàn)檫Btimer tick中斷也不打擾你了。
感謝各位的閱讀,以上就是“Linux高性能任務(wù)獨(dú)占CPU舉例分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Linux高性能任務(wù)獨(dú)占CPU舉例分析這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。