1. 程式人生 > 實用技巧 >linux-CPU 效能優化總結

linux-CPU 效能優化總結

在Linux系統中,由於成本的限制,往往會存在資源上的不足,例如 CPU、記憶體、網路、IO 效能。本文,就對 Linux 程序和 CPU 的原理進行分析,總結出 CPU 效能優化的方法。

1.分析手段

在理解平均負載之前,先要理清楚 Linux 下的程序狀態。

1.1.程序狀態

1.1.1. R (TASK_RUNNING),可執行狀態

只有在該狀態的程序才可能在 CPU 上執行。而同一時刻可能有多個程序處於可執行狀態,這些程序的 task_struct 結構(程序控制塊)被放入對應 CPU 的可執行佇列中(一個程序最多隻能出現在一個 CPU 的可執行佇列中)。程序排程器的任務就是從各個 CPU 的可執行佇列中分別選擇一個程序在該 CPU 上執行。

很多作業系統教科書將正在 CPU 上執行的程序定義為 RUNNING 狀態、而將可執行但是尚未被排程執行的程序定義為READY狀態,這兩種狀態在linux下統一為 TASK_RUNNING 狀態。

1.1.2. S (TASK_INTERRUPTIBLE),可中斷的睡眠狀態

處於這個狀態的程序因為等待某某事件的發生(比如等待 socket 連線、等待訊號量),而被掛起。這些程序的 task_struct 結構被放入對應事件的等待佇列中。當這些事件發生時

(由外部中斷觸發、或由其他程序觸發),對應的等待佇列中的一個或多個程序將被喚醒。通過 ps 命令我們會看到,一般情況下,程序列表中的絕大多數程序都處於 TASK_INTERRUPTIBLE 狀態(除非機器的負載很高)。畢竟 CPU 就這麼一兩個,程序動輒幾十上百個,如果不是絕大多數程序都在睡眠,CPU 又怎麼響應得過來。

1.1.3. D (TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態

與 TASK_INTERRUPTIBLE 狀態類似,程序處於睡眠狀態,但是此刻程序是不可中斷的。

不可中斷,指的並不是 CPU 不響應外部硬體的中斷,而是指程序不響應非同步訊號。

絕大多數情況下,程序處在睡眠狀態時,總是應該能夠響應非同步訊號的。否則你將驚奇的發現,kill -9 竟然殺不死一個正在睡眠的程序了!於是我們也很好理解,為什麼 ps 命令看到的程序幾乎不會出現 TASK_UNINTERRUPTIBLE 狀態,而總是 TASK_INTERRUPTIBLE 狀態。

而TASK_UNINTERRUPTIBLE狀態存在的意義就在於,核心的某些處理流程是不能被打斷的。如果響應非同步訊號,程式的執行流程中就會被插入一段用於處理非同步訊號的流程(這個插入的流程可能只存在於核心態,也可能延伸到使用者態),於是原有的流程就被中斷了。

(參見《linux 核心非同步中斷淺析》) 在程序對某些硬體進行操作時(比如程序呼叫 read 系統呼叫對某個裝置檔案進行讀操作,而 read 系統呼叫最終執行到對應裝置驅動的程式碼,並與對應的物理裝置進行互動),可能需要使用 TASK_UNINTERRUPTIBLE 狀態對程序進行保護,以避免程序與裝置互動的過程被打斷,造成裝置陷入不可控的狀態。這種情況下的 TASK_UNINTERRUPTIBLE 狀態總是非常短暫的,通過 ps 命令基本上不可能捕捉到。

1.1.4. T (TASK_STOPPED or TASK_TRACED),暫停狀態或跟蹤狀態

向程序傳送一個 SIGSTOP 訊號,它就會因響應該訊號而進入 TASK_STOPPED 狀態(除非該程序本身處於 TASK_UNINTERRUPTIBLE 狀態而不響應訊號)。(SIGSTOP 與 SIGKILL 訊號一樣,是非常強制的。不允許使用者程序通過 signal 系列的系統呼叫重新設定對應的訊號處理函式。)

向程序傳送一個 SIGCONT 訊號,可以讓其從 TASK_STOPPED 狀態恢復到 TASK_RUNNING 狀態。

當程序正在被跟蹤時,它處於 TASK_TRACED 這個特殊的狀態。"正在被跟蹤"指的是程序暫停下來,等待跟蹤它的程序對它進行操作。比如在 gdb 中對被跟蹤的程序下一個斷點,程序在斷點處停下來的時候就處於 TASK_TRACED 狀態。而在其他時候,被跟蹤的程序還是處於前面提到的那些狀態。

對於程序本身來說,TASK_STOPPED 和 TASK_TRACED 狀態很類似,都是表示程序暫停下來。而 TASK_TRACED 狀態相當於在 TASK_STOPPED 之上多了一層保護,處於 TASK_TRACED 狀態的程序不能響應 SIGCONT 訊號而被喚醒。只能等到除錯程序通過 ptrace 系統呼叫執行 PTRACE_CONT、PTRACE_DETACH 等操作(通過 ptrace 系統呼叫的引數指定操作),或除錯程序退出,被除錯的程序才能恢復 TASK_RUNNING 狀態。

1.1.5. Z (TASK_DEAD - EXIT_ZOMBIE),退出狀態,程序成為殭屍程序

程序在退出的過程中,處於 TASK_DEAD 狀態。在這個退出過程中,程序佔有的所有資源將被回收,除了 task_struct 結構(以及少數資源)以外。於是程序就只剩下 task_struct 這麼個空殼,故稱為殭屍。之所以保留 task_struct,是因為 task_struct 裡面儲存了程序的退出碼、以及一些統計資訊。而其父程序很可能會關心這些資訊。比如在 shell 中,$?變數就儲存了最後一個退出的前臺程序的退出碼,而這個退出碼往往被作為 if 語句的判斷條件。

當然,核心也可以將這些資訊儲存在別的地方,而將 task_struct 結構釋放掉,以節省一些空間。但是使用 task_struct 結構更為方便,因為在核心中已經建立了從 pid 到 task_struct 查詢關係,還有程序間的父子關係。釋放掉 task_struct,則需要建立一些新的資料結構,以便讓父程序找到它的子程序的退出資訊。

父程序可以通過 wait 系列的系統呼叫(如 wait4、waitid)來等待某個或某些子程序的退出,並獲取它的退出資訊。然後 wait 系列的系統呼叫會順便將子程序的屍體(task_struct)也釋放掉。

子程序在退出的過程中,核心會給其父程序傳送一個訊號,通知父程序來"收屍"。這個訊號預設是 SIGCHLD,但是在通過 clone 系統呼叫建立子程序時,可以設定這個訊號。

1.2.平均負載

單位時間內,系統處於可執行狀態和不可中斷狀態的平均程序數,也就是平均活躍程序數,它和 CPU 使用率並沒有直接關係。

既然是平均的活躍程序數,那麼最理想的,就是每個cpu 上都剛好執行著一個程序,這樣每個 cpu 都得到了充分利用,比如當平均負載 2時,意味著什麼呢?

1、 在只有2 個 CPU的系統上,意味著所有的 CPU都剛好被完全佔用

2、 在 4 個CPU的系統上,意味著 CPU 有 50%的空閒

3、 而在只有 1 個CPU 的系統上,則意味著有一半的程序競爭不到 CPU

1.2.1. 平均負載多少合理?

平均負載最理想的情況是等於CPU個數。檢視系統 CPU 的命令如下:

cat /proc/cpuinfo

Figure 1 四核 CPU 檢視平均負載的命令:

給了我們三個不同時間間隔的平均值,給我們提供了分析系統負載趨勢的資料來源,讓我們更全面、更立體地理解目前的負載情況。

· 1 分鐘、5 分鐘、15 分鐘 的三個值基本相同,或者相差不大,說明系統負載很平

· 如果1 分鐘的值遠小於15 分鐘 的值,說明系統最近 1 分鐘的負載在減少,而過去

15 分鐘內卻有很大的負載

· 如果1 分鐘 的值遠大於 15 分鐘的值,就說明最近 1 分鐘的負載在增加。一旦 1 分鐘的平均負載接近或超過了 CPU 的個數,就意味著系統正在發生過載的問題。

uptime 命令在有些嵌入式裝置中,會被裁減掉,但是可以通過 proc 檔案系統來獲取。命令:

cat /proc/loadavg

很顯然,當前命令展示的平均負載在 CPU 為 4 個時候已經過載

1.2.2. 平均負載與 CPU 使用率

平均負載不僅包括了正在使用CPU的程序,還包括了等待CPU和等待I/O的程序。

CPU 使用率是指單位時間內 CPU 繁忙情況的統計,跟平均負載並不一定完全對應。比如:

· CPU 密集型程序,使用大量 CPU 會導致平均負載升高,此時這兩者是一致的;

· I/O 密集型程序,等待 I/O 也會導致平均負載升高,但 CPU 使用率不一定很高

· 大量等待 CPU 的程序排程也會導致平均負載升高,此時的 CPU 使用率也會比較高。

1.3.CPU 上下文切換

在每個任務執行前, CPU 都需要知道任務從哪裡載入、又從哪裡開始執行、也就是說,需要系統事先給他設定好CPU暫存器和程式計數器(Program Counter, PC)

·CPU暫存器:是 CPU 內建的容量小、但速度極快的記憶體。

· 程式計數器:是用來儲存 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。

它們都是 CPU 在執行任何任務前,比如的依賴環境,因此也被叫做CPU上下文。

· 上下文切換:就是先把前一個任務的 CPU 上下文(也就是 CPU 暫存器和程式計數器)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器,最後再跳轉到程式計數器所指的新位置,執行新任務。

· CPU 的上下文切換可以分為程序上下文切換、執行緒上下文切換以及中斷上下文切換。

1.3.1. 程序上下文切換

Linux 按照特權等級,把程序的執行空間分為核心空間和使用者空間

· 核心空間(Ring 0)具有最高許可權,可以直接訪問所有資源。

· 使用者空間(Ring 3)只能訪問受限資源,不能直接訪問記憶體等硬體裝置,必須通過系統呼叫陷入到核心中,才能訪問這些特權資源。

1.3.2. 程序上下文切換和系統呼叫的區別

程序是由核心來管理和排程的,程序的切換隻能發生在核心態。所以,程序的上下文不僅包括了虛擬記憶體、棧、全域性變數等使用者空間的資源,還包括了核心堆疊、暫存器等核心空間的狀態。

系統呼叫過程中,並不涉及到虛擬記憶體等程序使用者態的資源,也不會切換程序。

· 程序上下文切換,是指從一個程序切換到另一個程序進行。

· 系統呼叫過程中一直是同一個程序在執行。

因此,程序的上下文切換比系統呼叫時多了一步:在儲存當前程序的核心狀態和 CPU 暫存器之前,需要先把該程序的虛擬記憶體、棧等儲存下來;而載入了下一個程序的核心態後,還需要重新整理程序的虛擬記憶體和使用者棧。

1.3.3. 什麼時候會切換程序上文

· 程序執行終止,它之前使用的 CPU 會釋放出來,這時再從就緒佇列裡,拿一個新的程序過來執行。

· 當某個程序的時間片耗盡了,就會被系統掛起,切換到其他正在等待 CPU 的程序進行

· 程序在系統資源不足(比如記憶體不足)時,等到資源滿足後才可以執行,這個時候程序也會被掛起,並由系統排程其他程序執行。

· 當程序通過睡眠函式 sleep 這樣的方法將自己主動掛起時,自然也會重新排程。

· 當有優先順序更高的程序執行時,為了保證高優先順序程序的執行,當前程序會被掛起,由高優先順序程序來執行。

· 發生硬體中斷時,CPU 上的程序會被中斷掛起,轉而執行核心中的中斷程式服務。

1.3.4. 執行緒上下文切換

執行緒和程序的區別

· 執行緒是排程的基本單位,而程序則是資源擁有的基本單位。

· 當程序只有一個執行緒時,可以認為程序就等於執行緒。

· 當程序擁有多個執行緒時,這些執行緒會共享相同的虛擬記憶體和全域性變數等資源。這些資源在上下文切換時是不需要修改的。

· 執行緒也有自己的私有資料,比如棧和暫存器等,這些在上下文切換時也是需要儲存的。

執行緒的上下文切換兩種情況

· 前後兩個執行緒屬於不同程序。此時,因為資源不共享,所以切換過程就跟程序上下文切換是一樣的。

· 前後兩個執行緒屬於同一個程序。此時,因為虛擬記憶體是共享的,所以在切換時,虛擬記憶體這些資源就保持不動,只需要切換執行緒的私有資料、暫存器等不共享的資料。

1.3.5. 中斷上下文切換

中斷處理會打斷程序的正常排程和執行。在打斷其他程序時,需要將程序當前的狀態儲存下來,中斷結束後,程序仍然可以從原來的狀態恢復執行。

程序上下文切換和中斷上下文切換的區別

· 中斷上下文切換並不涉及到程序的使用者態。所以,即便中斷過程打斷了一個正處在使用者態的程序,也不需要儲存和恢復這個程序的虛擬記憶體、全域性變數等使用者態資源。中斷上下文,其實只包括核心態中斷服務程式執行所必須的狀態,包括 CPU 暫存器、核心堆疊、硬體中斷引數等。

· 對同一個 CPU 來說,中斷處理比程序擁有更高的優先順序。

程序上下文切換和中斷上文切換的相同之處

· 都需要消耗 CPU,切換次數過多會耗費大量 CPU,甚至嚴重降低系統的整體效能。

1.3.6. CPU 上下文切換小結

· CPU 上下文切換,是保證 Linux 系統正常工作的核心功能之一,一般情況下不需要我們特別關注。

· 但過多的上下文切換,會把 CPU 時間消耗在暫存器、核心棧以及虛擬記憶體等資料的儲存和恢復上,從而縮短程序真正執行的時間,導致系統的整體效能大幅下降

1.3.7. 如何檢視系統的上下文切換

常用的系統性能分析工具,主要用來分析系統的記憶體使用情況,也常用來分析

CPU 上下文切換和中斷次數。

Figure 2 每隔 2 秒輸出一組資料

需要特別關注的四列內容:

· cs (context switch):每秒上下文切換的次數。

· in (interrupt):每秒中斷的次數。

· r (Running or Runnable) :就緒佇列的長度,也就是正在執行和等待 CPU 的程序數。

· b (Blocked):處在不可中斷睡眠狀態的程序數。

在嵌入式 Linux 裝置中,一般 vmstat 工具是不存在的。所以如果想要 vmstat 工具,可以自己實現程式碼,他的原理是獲取/proc/diskstats 和/proc/slabinfo 的資訊組合而成。實現程式碼見 procps 工具

vmstat 只給出了系統總體的上下文切換情況,並不能檢視每個程序的上下文切換情況。

:檢視某個程序中執行緒的上下文切換情況,下圖檢視的是 hicore 程序中所有的執行緒上下文切換情況。

關注兩列內容:

1. 自願上下文切換:程序無法獲取所需資源,導致的上下文切換。比如, I/O、記憶體等系統資源不足時。

2. 非自願上下文切換:程序由於時間片已到等原因,被系統強制排程,進而發生的上下文切換。比如,大量程序都在爭搶 CPU 時。

1.3.9. Procps 工具

procps 是一組命令列和全屏工具,是由核心動態生成的一個 "偽" 檔案系統,可以提供程序表中條目狀態的資訊。該檔案系統為核心資料結構提供了一個簡易介面,procps 程式通常就集中在這個描述了系統程序執行狀態的資料結構上。

procps 包括以下程式:

· free - 報告系統中可用記憶體和已用記憶體的數量

· kill - 基於 PID,向程序傳送訊號

· pgrep - 根據名稱或其他屬性列出程序

· pkill - 根據名稱或其他屬性向程序傳送訊號

· pmap - 報告程序的記憶體對映

· ps - 報告程序資訊

· pwdx - 報告程序的當前目錄

· skill - pgrep/pkill 的過時版本

· slabtop - 實時顯示核心 slab 快取資訊

· snice - Renice 一個程序

· sysctl -執行時核心引數的讀或寫

· tload - 系統負載均值的視覺化

· top - 正執行程序的實時動態檢視

· uptime - 顯示系統的已執行時間和負載情況

· vmstat - 報告虛擬記憶體統計資訊

· w - 報告登入使用者,以及他們正在做什麼

· watch - 定期執行程式,顯示全屏輸出官網地址:http://procps.sourceforge.net/

1.3.10. sysstat 工具

在嵌入式 Linux 裝置中同樣也不存在該工具,busybox 中也沒有相關命令。需要安裝 Linux 效能監控工具 sysstat,他是一個工具集,包括 sar、sadf、mpstat、iostat、pidstat 等,這些工具可以監控系統性能和使用情況。各工具的作用如下:

1. iostat - 提供 CPU 統計,儲存 I/O 統計(磁碟裝置,分割槽及網路檔案系統)

2. mpstat - 提供單個或組合 CPU 相關統計

3. pidstat - 提供 Linux 程序級別統計:I/O、CPU、記憶體等

4. sar - 收集、報告、儲存系統活動資訊:CPU、記憶體、磁碟、中斷、網路介面、TTY、核心表等

5. sadc - 系統活動資料收集器,作為 sar 後端使用

6. sa1 - 收集系統活動日常資料,並二進位制格式儲存,它作為 sadc 的工具的前端,可以通過 cron 來呼叫

7. sa2 - 生成系統每日活動報告,同樣可作為 sadc 的工具的前端,可以通過 cron 來呼叫

8. sadf - 可以以 CSV、XML 格式等顯示 sar 收集的效能資料,這樣非常方便的將系統資料匯入到資料庫中,或匯入到 Excel 中來生成圖表

9. nfsiostat-sysstat: 提供 NFS I/O 統計

10. cifsiostat: 提供 CIFS 統計

sysstat 功能強大,功能也在不斷的增強,每個版本提供了不同的功能,可以到 sysstat 官網了 解 工 具 最 先 發 展 情 況 和 獲 得 相 應 的 幫 助 手 冊 。官 網 地 址 :

1.3.11. 中斷

中斷是一種非同步的事件處理機制,可以提高系統的併發處理能力。中斷處理程式會打斷其他程序的執行,為了減少對正常程序執行排程的影響,中斷處理程式就需要儘可能快地執行。

Linux 將中斷處理過程分成了兩個階段,也就是上半部和下半部。

· 上半部用來快速處理中斷,它在中斷禁止模式下執行,主要處理跟硬體緊密相關的或時間敏感的工作。

· 下半部用來延時處理上半部未完成的工作,通常以核心執行緒的方式執行。

/proc/interrupts:檢視硬中斷髮生的型別

硬體中斷髮生頻繁,是件很消耗 CPU 資源的事情,Linux 預設情況下是將所有的硬體中斷都繫結在 CPU0 上,在多核 CPU 條件下如果有辦法把大量硬體中斷分配給不同的

CPU (core) 處理顯然能很好的平衡效能。

1.3.13. 根據上下文切換的型別做具體分析

· 自願上下文切換變多,說明程序都在等待資源,有可能發生 I/O 等其他問題

· 非自願上下文切換變多,說明程序都在被強制排程,也就是都在爭搶 CPU,說明 CPU 的確成了瓶頸

· 中斷次數變多,說明 CPU 被中斷處理程式佔用,還需要通過檢視 /proc/interrupts 檔案來分析具體的中斷型別。

1.4.CPU 使用率

/proc/stat,提供的是系統的 CPU 和任務統計資訊。這個資訊非常的原始

CPU使用率相關的重要指標

· 第一列:user(us),代表使用者態 CPU 時間。

· 第二列:nice(ni),代表低優先順序使用者態 CPU 時間,也就是程序的 nice 值被調整為 1-

19 之間時的 CPU 時間。nice 可取值範圍是 -20 到 19, 數值越大,優先順序反而越低

· 第三列:system (sys),代表核心態 CPU 時間。

· 第四列:idle(us),代表空閒時間。注意,這裡它不包括等待 I/O 的時間(iowait)。

· 第五列:iowait(wa),代表等待 I/O 的 CPU 時間。

· 第六列:irq(hi),代表處理硬中斷的 CPU 時間

· 第七列:softirq(si),代表處理軟中斷的 CPU 時間。

真正檢視 CPU 使用率的命令是通過 top 命令

1.5.軟中斷

提供了軟中斷的執行情況。

· 注意軟中斷的型別,也就是第一列內容。

· 注意同一種軟中斷在不同 CPU 上的分佈情況,也就是同一行內容。

· 軟中斷實際上是以核心執行緒的方式執行的,每個 CPU 都對應一個軟中斷核心執行緒,這個軟中斷核心執行緒就叫做 ksoftirqd/CPU 編號

2.優化方法

2.1 CPU 使用率

CPU 使用率描述了非空閒時間佔總 CPU 時間的百分比,根據 CPU 上執行任務的不同,又被分為使用者 CPU、系統 CPU、等待 I/O CPU、軟中斷和硬中斷等。

· 使用者 CPU 使用率,包括使用者態 CPU 使用率(user) 和低優先順序使用者態 CPU 使用率 (nice),表示 CPU 在使用者態執行的時間百分比。使用者 CPU 使用率高,通常說明有應用程式比較繁忙。

· 系統 CPU 使用率,表示 CPU 在核心態執行的時間百分比(不包括中斷)。系統 CPU 使用率高,說明核心比較繁忙。

· 等待 I/O 的 CPU 使用率,通常也稱為 iowait,表示等待 I/O 的時間百分比。iowait 高,通常說明系統與硬體裝置的 I/O 互動時間比較長。

· 軟中斷和硬中斷的 CPU 使用率,分別表示核心呼叫軟中斷處理程式、硬中斷處理程式的時間百分比。它們的使用率高,通常說明系統發生了大量的中斷。

2.2平均負載(Load Average)

平均負載,也就是系統的平均活躍程序數,它反映了系統的整體負載情況,主要包括三個數值,分別指過去 1 分鐘、過去 5 分鐘和過去 15 分鐘的平均複製子。

理想情況下,平均負載等於邏輯 CPU 個數,這表示每個 CPU 都恰好被充分利用。如果平均負載大於邏輯 CPU 個數,就表示負載比較重了。

2.3程序上下文切換

· 無法獲取資源而導致的自願上下文切換。

· 被系統強制排程導致的非自願上下文切換。

2.4CPU 快取的命中率

由於 CPU 發展的速度遠快於記憶體的發展, CPU 的處理速度就比記憶體的訪問速度快得多。這樣,CPU 在訪問記憶體的時候,免不了要等待記憶體的響應。為了協調這兩者巨大的效能差距,CPU 快取(通常是多級快取)就出現了。

根據不斷增長的熱點資料,這些快取按照大小不同分為 L1、L2、L3 等三級快取,其中

L1 和 L2 常用在單核中,L3 則用在多核中。

從 L1 到 L3,三級快取的大小依次增大,相應的,效能依次降低(當然比記憶體還是好

得多)。而它們的命中率,衡量的是 CPU 快取的複用情況,命中率越高,則表示效能越好。

2.5tcmalloc 替換 ptmalloc

2.5.1 ptmalloc

Ptmalloc 採用主-從分配區的模式,當一個執行緒需要分配資源的時候,從連結串列中找到一個沒加鎖的分配區,在進行記憶體分配。

小記憶體分配

在ptmalloc內部,記憶體塊採用chunk管理,並且將大小相似的 chunk 用連結串列管理,一個連結串列被稱為一個 bin。前 64 個 bin 裡,相鄰的 bin 內的 chunk 大小相差 8 位元組,稱為small bin,後面的是large bin,large bin 裡的 chunk 按先大小,再最近使用的順序排列,每次分配都找一個最小的能夠使用的 chunk。

Chunk的結構如上所示,A位表示是不是在主分配區,M表示是不是mmap出來的,P表示上一個記憶體緊鄰的chunk是否在使用,如果沒在使用,則 size of previous

chunk 是上一個 chunk 的大小,否則無意義(而且被用作被分配出去的記憶體了),正式根據

P 標記位和size of previous chunk在 free 記憶體塊的時候來進行 chunk 合併的。當然,如果 chunk 空閒,mem 裡還記錄了一些指標用於索引臨近大小的 chunk 的,實現原理就不復述了,知道大致作用就行。

在 free 的時候,ptmalloc 會檢查附近的 chunk,並嘗試把連續空閒的 chunk 合併成一個大的 chunk,放到 unstored bin 裡。但是當很小的 chunk 釋放的時候,ptmalloc 會把它併入 fast bin 中。同樣,某些時候,fast bin 裡的連續記憶體塊會被合併並加入到一個 unsorted bin 裡,然後再才進入普通 bin 裡。所以 malloc 小記憶體的時候,是先查詢fast

bin,再查詢unsorted bin,最後查詢普通的bin,如果 unsorted bin 裡的 chunk 不合適,則會把它扔到 bin 裡。

大記憶體分配

Ptmalloc的分配的記憶體頂部還有一個 top chunk,如果前面的bin裡的空閒chunk都不足以滿足需要,就是嘗試從top chunk裡分配記憶體。如果 top chunk 裡也不夠,就要從作業系統裡拿了。

還有就是特別大的記憶體,會直接從系統mmap出來,不受 chunk 管理,這樣的記憶體在回收的時候也會 munmap 還給作業系統。

簡而言之,就是:

小記憶體:[獲取分配區(arena)並加鎖] -> fast bin -> unsorted bin -> small bin -> large bin

-> top chunk -> 擴充套件堆

大記憶體:直接 mmap

總結

釋放的時候,幾乎是和分配反過來,再加上可一些 chunk 合併和從一個 bin 轉移到另一個 bin 的操作。並且如果頂部有足夠大的空閒 chunk,則收縮堆頂並還給作業系統。

介於此,對於 ptmalloc 的記憶體分配使用有幾個注意事項:

1. Ptmalloc 預設後分配記憶體先釋放,因為記憶體回收是從 top chunk 開始的。

2. 避免多執行緒頻繁分配和釋放記憶體,會造成頻繁加解鎖。

3. 不要分配長生命週期的記憶體塊,容易造成內碎片,影響記憶體回收。

2.5.2 Tcmalloc

具體實現原理不加以贅述,可自行百度學習之,總結以下特點。

· Tcmalloc 佔用更少的額外空間。例如,分配 N 個 8 位元組物件可能要使用大約 8N * 1.01

位元組的空間。即,多用百分之一的空間。Ptmalloc2 使用最少 8 位元組描述一個 chunk。

· 更快。小物件幾乎無鎖, >32KB 的物件從 CentralCache 中分配使用自旋鎖。並且>32KB 物件都是頁面對齊分配,多執行緒的時候應儘量避免頻繁分配,否則也會造成自旋鎖的競爭和頁面對齊造成的浪費。

2.6思維導圖

3.分析工具

從 CPU 的效能指標出發。當你要檢視某個效能指標時,要清楚知道哪些工具可以做到。

4.思路

效能優化並不是沒有副作用的,通常情況下 Linux 系統是不需要特意調整某些指標。往往效能優化會帶來整體系統的複雜度的上升,降低了可移植性,也可能在調整某個指標的時候導致其他指標異常。

並不是所有的效能問題都需要去優化,需要對瓶頸點進行優化。比如當前系統有瓶頸,使用者 CPU 使用率升高了 10%,而系統系統 CPU 使用率卻升高了 50%,這個時候就應該首先優化系統 CPU 使用率。

4.1應用程式優化

從應用程式的角度來說,降低 CPU 使用率最好的方法是,排除所有不必要的工作,只保留最核心的邏輯。比如減少迴圈層次、減少遞迴、減少動態記憶體分配等等。

常見的幾種應用程式的效能優化方法:

· 編譯器優化:很多編譯器都會提供優化選項,適當開啟它們,在編譯階段你就可以獲得編譯器的幫助,來提升效能。目前裝置採用的優化選項為 Os,相當於 O2.5

· 演算法優化:使用非同步處理,可以避免程式因為等待某個資源而一直阻塞,從而提升程式的併發處理能力。

· 多執行緒代替多程序:執行緒的上下文切換並不切換程序地址空間,因此可以降低上下文切換的成本。目前裝置採用的是多執行緒模式。

· 使用 buffer:經常訪問的資料,可以放在記憶體中快取起來,這樣在下次用時可以直接從記憶體中獲取,加快程式的處理速度。

· 小記憶體使用:小記憶體的申請,在保證棧空間不溢位的情況下,儘量採用棧上申請,少使用動態記憶體申請,提高程式執行效率。

4.2系統優化

從系統的角度來說,優化 CPU 的執行,一方面要充分利用 CPU 快取的本地性,加速快取訪問;另一方面,就是要控制程序的 CPU 使用情況,減少程序間的相互影響。

常見的方法:

· CPU 繫結:把程序繫結到一個或者多個 CPU 上,可以提高 CPU 快取的命中率,減少跨 CPU 排程帶來的上下文切換問題。

· 優先順序調整:使用 nice 調整程序的優先順序,正值調低優先順序,負值調高優先順序。

· 中斷負載均衡:無論是軟中斷還是硬中斷,它們的中斷處理程式都可能消耗大量的 CPU。

配置 smp_affinity,就可以把中斷處理過程自動負載均衡到其他 CPU 上

· 替換 ptmalloc:當前使用的 gblic 庫,其動態記憶體管理採用就是 ptmalloc。當前應用程式大量使用小尺寸動態記憶體,可以採用 tcmalloc,在小記憶體上更快,幾乎無鎖。