簡述優(yōu)化 Linux 內(nèi)存性能的核心思想
原文:linux網(wǎng)絡(luò)虛擬化
今天分享一篇內(nèi)存性能優(yōu)化的文章,文章用了大量精美的圖深入淺出地分析了 Linux 內(nèi)核 slab 性能優(yōu)化的核心思想。
slab是 Linux 內(nèi)核小對象內(nèi)存分配最重要的算法,文章分析了內(nèi)存分配的各種性能問題(在不同的場景下面),并給出了這些問題的優(yōu)化方案,這個對我們實現(xiàn)高性能內(nèi)存池算法,或以后遇到內(nèi)存性能問題的時候,有一定的啟發(fā),值得我們學習。
單CPU上單純的slab
下圖給出了單 CPU 上 slab 在分配和釋放對象時的情景序列:
可以看出,非常之簡單,而且完全達到了slab設(shè)計之初的目標。
擴展到多核心CPU
現(xiàn)在我們簡單地將上面的模型擴展到多核心CPU,同樣差不多的分配序列如下圖所示:
問題
首先,我們來看一個簡單的問題,如果單獨的某個CPU的 slab 緩存沒有對象可分配了,但是其它 CPU 的 slab 緩存仍有大量空閑對象的情況,如下圖所示:
這是可能的,因為對單獨一種slab的需求是和該CPU上執(zhí)行的進程/線程緊密相關(guān)的,比如 如果CPU0只處理網(wǎng)絡(luò),那么它就會對skb等數(shù)據(jù)結(jié)構(gòu)有大量的需求,對于上圖最后引出的問題,如果我們選擇從伙伴系統(tǒng)中分配一個新的page(或者pages,取決于對象大小以及slab cache的order),那么久而久之就會造成slab在CPU間分布的不均衡,更可能會因此吃掉大量的物理內(nèi)存,這都是不希望看到的。
問題的解決-分層slab cache
無級變速總是讓人向往。如果一個CPU的slab緩存滿了,直接去搶同級別的別的CPU的slab緩存被認為是一種魯莽且不道義的做法。
那么為何不設(shè)置另外一個slab緩存,獲取它里面的對象不像直接獲取CPU的slab緩存那么簡單且直接,但是難度卻又不大,只是稍微增加一點消耗,這不是很好嗎?
事實上,CPU的L1,L2,L3 cache不就是這個方案設(shè)計的嗎?這事實上已經(jīng)成為cache設(shè)計的不二法門。這個設(shè)計思想同樣作用于slab,就是Linux內(nèi)核的slub實現(xiàn),現(xiàn)在可以給出概念和解釋了。
Linux kernel slab cache:一個分為3層的對象cache模型。
Level 1 slab cache:一個空閑對象鏈表,每個CPU一個的獨享cache,分配釋放對象無需加鎖。
Level 2 slab cache:一個空閑對象鏈表,每個CPU一個的共享page(s) cache,分配釋放對象時僅需要鎖住該page(s),與Level 1 slab cache互斥,不互相包容。
Level 3 slab cache:一個page(s)鏈表,每個NUMA NODE的所有CPU共享的cache,單位為page(s),獲取后被提升到對應(yīng)CPU的Level 1 slab cache,同時該page(s)作為Level 2的共享page(s)存在。
共享page(s):該page(s)被一個或者多個CPU占有,每一個CPU在該page(s)上都可以擁有互相不充圖的空閑對象鏈表,該page(s)擁有一個唯一的Level 2 slab cache空閑鏈表,該鏈表與上述一個或多個Level 1 slab cache空閑鏈表亦不沖突,多個CPU獲取該Level 2 slab cache時必須爭搶,獲取后可以將該鏈表提升成自己的Level 1 slab cache。
其行為如下圖所示:
2個場景
事實上,對于多個CPU共享一個page(s)的情況,還可以有另一種玩法,如下圖所示:
伙伴系統(tǒng)
盡量分配盡可能大的內(nèi)存
盡量合并連續(xù)的小塊內(nèi)存成一塊大內(nèi)存
我們可以通過下面的圖解來理解上面的原則:
小結(jié)
多CPU操作系統(tǒng)內(nèi)核中,關(guān)鍵的開銷就是鎖的開銷。
我認為這是一開始的設(shè)計導致的,因為一開始,多核CPU并沒有出現(xiàn),單核CPU上的共享保護幾乎都是可以用“禁中斷”,“禁搶占”來簡單實現(xiàn)的,到了多核時代,操作系統(tǒng)同樣簡單平移到了新的平臺,因此同步操作是在單核的基礎(chǔ)上后來添加的。
簡單來講,目前的主流操作系統(tǒng)都是在單核年代創(chuàng)造出來的,因此它們都是順應(yīng)單核環(huán)境的,對于多核環(huán)境,可能它們一開始的設(shè)計就有問題。
不管怎么說,優(yōu)化操作的不二法門就是禁止或者盡量減少鎖的操作。隨之而來的思路就是為共享的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)創(chuàng)建"每CPU的緩存“,而這類緩存分為兩種類型:
比如路由表之類的數(shù)據(jù)結(jié)構(gòu),你可以用RCU鎖來保護,當然如果為每一個CPU都創(chuàng)建一個本地路由表緩存,也是不錯的,現(xiàn)在的問題是何時更新它們,因為所有的緩存都是平級的,因此一種批量同步的機制是必須的。
2. 管理機制緩存
比如slab對象緩存這類,其生命周期完全取決于使用者,因此不存在同步問題,然而卻存在管理問題。
采用分級cache的思想是好的,這個非常類似于CPU的L1/L2/L3緩存,采用這種平滑的開銷逐漸增大,容量逐漸增大的機制,并配合以設(shè)計良好的換入/換出等算法,效果是非常明顯的。
最近很多小伙伴找我要一些程序員必備資料,于是我翻出了壓箱底的寶藏,免費分享給大家!
掃描海報二維碼免費獲取。


