《嵌入式Linux內(nèi)存與性能詳解》筆記4——性能優(yōu)化

一、前言

前面講了關(guān)于 內(nèi)存 方面的優(yōu)化,那么接下來的文章我們主要聚焦于 性能 的優(yōu)化,那么主要體現(xiàn)在優(yōu)化程序 速度 上。程序的 速度 很大程度上會影響用戶體驗或者程序的實際效用,所以優(yōu)化 性能速度 也是程序員需要關(guān)注的一個方面,從本文起接下來的幾篇文章將講述如何對 程序性能 進行優(yōu)化。

二、優(yōu)化思想

在講解優(yōu)化之前,我們先看一下 優(yōu)化的思想,它可以保證我們在學習或者優(yōu)化的過程中保持對問題的關(guān)注,讓我們知道是在學習什么跟如何優(yōu)化。

2.1 性能分析瓶頸分類

說到優(yōu)化,一個程序運行慢可能是有多種原因構(gòu)成的,那么前輩們就將這多種原因歸結(jié)為以下 3類

  • 程序的運算量很大,導致 CPU 過于繁忙。稱為 CPU 瓶頸。
  • 程序需要做大量的 I/O,讀寫文件、內(nèi)存操作等等,CPU 更多的是處于等待。稱為 I/O 瓶頸
  • 程序之間相互等待,結(jié)果 CPU 利用率很低,但運行速度依然很慢,事務(wù)間的共享與死鎖制約了程序的性能

2.2 優(yōu)化基本原則

  • 等效原則:優(yōu)化前后程序?qū)崿F(xiàn)的功能一致。
  • 有效原則:優(yōu)化后要比優(yōu)化前運行速度快或占用存儲空間小,或二者兼有。
  • 經(jīng)濟原則:優(yōu)化程序要付出較小的代價,取得較好的結(jié)果。

另外一般有 5 個基本綱領(lǐng)為我們提供優(yōu)化思路:

  • Do it faster: 程序除了 實現(xiàn)功能 之外,還需要 提高其運行速度。
  • Do it in parallel:能夠 并行執(zhí)行程序 的話,我們就可以 充分利用CPU資源,從而加速程序運行速度。
  • Do it later:對于時間緊迫的功能,有些不必要的功能,可以考慮延后執(zhí)行,使程序的任務(wù)減輕,從 而騰出資源,來做重要的事。
  • Don't do it at all:設(shè)計程序時,不要加入多余無用的代碼 來實現(xiàn) *模塊化或者提高可讀性
  • Do it before:程序有可能并不需要一直工作,我們可以把一些工作在 系統(tǒng)空閑 時完成,這樣可以減輕繁忙時程序的負擔。

2.3 優(yōu)化思路

在程序優(yōu)化上,往往 20%的代碼 占用了 80%的運行時間,所以我們需要找出這 20% 的代碼。所以 如何找出需要優(yōu)化的代碼 是一個我們需要關(guān)注的問題,而這個問題我們可以有一些工具來解決,在下面的章節(jié)會提到。

第一步:找出 性能瓶頸的代碼,在筆者看來,分析程序問題,找出性能瓶頸是重中之重。
第二步:進行 代碼優(yōu)化

2.4 優(yōu)化經(jīng)驗總結(jié):

下面是書中提到的一些 優(yōu)化經(jīng)驗,在了解這些經(jīng)驗之前,我們需要了解幾個概念:

  • 提高程序的效率:程序的 時間效率是指運行速度
  • 空間效率:是指程序 占用內(nèi)存或者外存的狀況
  • 全局效率 是指站在 整個系統(tǒng)的角度上考慮的效率
  • 局部效率 是指站在 模塊或函數(shù)角度上考慮 的效率

書中經(jīng)驗總結(jié)如下:

  • 不要一味地追求程序的效率,應(yīng)當在滿足 正確性、可靠性、健壯性、可讀性 等質(zhì)量因素的前提下,設(shè)法提高程序的效率。
  • 以提高程序的 全局效率為主,提高 局部效率為輔
  • 在優(yōu)化程序的效率時,應(yīng)當先找出限制效率的 瓶頸,不要在無關(guān)緊要之處優(yōu)化。
  • 先優(yōu)化 數(shù)據(jù)結(jié)構(gòu)和算法,再優(yōu)化 執(zhí)行代碼
  • 有時候 時間效率空間效率 可能對立,此時應(yīng)當分析那個更重要,作出適當?shù)恼壑浴?strong>例如多花費一些內(nèi)存來提高性能。
  • 不要追求 緊湊的代碼,因為 緊湊的代碼 并不能產(chǎn)生 高效的機器碼。
  • 當心那些視覺上不易分辨的操作符發(fā)生 書寫錯誤。 比如會把 == 誤寫成 =,像 ||、&&<=、>= 這些符號也很容易發(fā)生 丟1失誤。然而 編譯器卻不一定能自動指出這類錯誤
  • 變量(指針、數(shù)組) 被創(chuàng)建之后 應(yīng)當及時把它們初始化,以 防止把未被初始化的變量當成右值使用。
  • 當心變量的 初值、缺省值錯誤,或者精度不夠
  • 當心 數(shù)據(jù)類型轉(zhuǎn)換發(fā)生錯誤。盡量使用 顯式 的數(shù)據(jù)類型轉(zhuǎn)換,避免讓 編譯器輕悄悄地進行隱式的數(shù)據(jù)類型轉(zhuǎn)換
  • 當心變量發(fā)生 上溢或下溢,數(shù)組的 下標越界。
  • 當心忘記編寫 錯誤處理程序,且要注意防止 錯誤處理程序本身有誤
  • 當心 文件 I/O 有錯誤。
  • 避免 編寫技巧性很高代碼
  • 不要設(shè)計 面面俱到、非常靈活的數(shù)據(jù)結(jié)構(gòu)。
  • 如果原有的代碼質(zhì)量比較好,盡量復用它。但是 不要修補很差勁的代碼, 應(yīng)當重新編寫。
  • 盡量使用 標準庫函數(shù),不要 發(fā)明已經(jīng)存在的庫函數(shù)。
  • 盡量不要 使用與具體硬件或軟件環(huán)境關(guān)系密切的變量。
  • 把編譯器的選擇項設(shè)置為 嚴格狀態(tài)
  • 如果可能的話,使用 PC-Lint、LogiScope 等工具進行代碼審查。

2.5 優(yōu)化的層次

在我們了解到了 程序瓶頸 之后,接下來我們就要考慮對代碼進行優(yōu)化:

程序的優(yōu)化,主要包括四個層次:

  1. 算法和數(shù)據(jù)結(jié)構(gòu)的優(yōu)化
  2. 編譯器優(yōu)化
  3. 代碼優(yōu)化
  4. 硬加速

優(yōu)化新手容易犯的錯誤就是:找到 程序熱點 時,過于興奮,急匆匆的就開始 局部代碼 的優(yōu)化。但我們需要在 更大的范圍內(nèi)分析,是什么導致這個 程序瓶頸 的存在,有時可能根本就不需要改動你的程序。

三、性能評估

一般情況下,我們可以把 性能優(yōu)化 分為 2 個部分

  • 性能評估:一般而言,程序都有 二八法則。20%的代碼影響80%的速度。我們需要找出這20% 的代碼進行優(yōu)化,對其進行 瓶頸分析,找出影響速度的原因。
  • 性能優(yōu)化:找出原因和需要優(yōu)化的代碼后,利用 編譯、硬件算法和數(shù)據(jù)結(jié)構(gòu) 等對其進行優(yōu)化。

3.1 系統(tǒng)及進程性能評估

據(jù)上面所講,性能評測 是至關(guān)重要的一個環(huán)節(jié),如果不知道哪里出了問題,那么優(yōu)化就無從下手。程序性能的問題是有很多原因的,我們先看看一般的問題分析:

  • /proc目錄下系統(tǒng)相關(guān)的文件
  • /proc目錄下進程相關(guān)的文件

3.1.1 proc系統(tǒng)相關(guān)

系統(tǒng)相關(guān)屬性 我們可以通過下面 2 個文件來了解。

3.1.1.1. /proc/stat

我們使用 cat /proc/stat 來獲取 系統(tǒng)相關(guān) 的信息,這樣可以知道我們 系統(tǒng)效率 的具體細節(jié)

運行結(jié)果.png

下面 按行 說明各行數(shù)據(jù)的意義:

  • cpuN:其數(shù)值分別代表著 CPU不同狀態(tài) 下所用的時間,其單位為 jiffies(0.01s)
    1. user:從 系統(tǒng)啟動 開始累計到 當前時刻,用戶態(tài)的 CPU時間,不包含 nice值為負 的進程。 在圖中的值為 19。
    2. nice:從 系統(tǒng)啟動 開始累計到 當前時刻nice值為負 的進程所占用的 CPU時間。 在圖中的值為 0。
    3. system:從 系統(tǒng)啟動開始累計到 當前時刻,內(nèi)核 所占用的 CPU時間。 在圖中的值為 166
    4. idle:從 系統(tǒng)啟動 開始累計到 當前時刻,除 硬盤IO等待時間 的以外其它等待時間。 在圖中的值為 218632。
    5. iowait:從 系統(tǒng)啟動 開始累計到 當前時刻,硬盤IO 等待時間。 在圖中的值為 73。
    6. irq:從 系統(tǒng)啟動 開始累計到 當前時刻,硬中斷時間。在圖中的值為 0
    7. softirq:從 系統(tǒng)啟動 開始累計到 當前時刻,軟中斷時間。在圖中的值為 0。
    8. stealstolen:在 虛擬化環(huán)境中 運行時在 其他操作系統(tǒng) 上花費的時間
    9. guestLinux內(nèi)核 控制下為 虛擬操作系統(tǒng) 運行 虛擬CPU 所花費的時間
  • intr:從 系統(tǒng)啟動 開始累計到 當前時刻,發(fā)生的 所有中斷的次數(shù)。每個數(shù) 對應(yīng)一個特定的 中斷。
  • ctxt:從 系統(tǒng)啟動 開始累計到 當前時刻CPU 發(fā)生的 上下文交換 的次數(shù)。
  • btime:從 系統(tǒng)啟動 開始累計到 當前時刻時間,單位為秒。
  • processes:從 系統(tǒng)啟動 開始累計到 當前時刻 ,系統(tǒng)所創(chuàng)建的 任務(wù)數(shù)目。
  • procs_running:當前 運行隊列 的任務(wù)數(shù)目。
  • procs_blocked:當前 被阻塞 的任務(wù)數(shù)目。

3.1.1.2. CPU利用率

CPU利用率是指CPU工作時間占總時間的比重, 簡單地理解為 單位時間內(nèi) CPU處于忙狀態(tài)的時間占比。

摘抄附錄《CPU利用率與Load Average的區(qū)別?》中的說法:
CPU利用率 可以看出在某一個時間段內(nèi)CPU被占用的情況,如果CPU被占用時間很高,那么就需要考慮CPU是否已經(jīng)處于超負荷運作,長期超負荷運作對于機器本身來說是一種損害,因此必須將CPU的利用率控制在一定的比例下,以保證機器的正常運作。

有一種情況需要注意的是:使用 自旋鎖忙等處理的鎖會導致 CPU利用率的上升,此時 CPU利用率 并不能很好的反映 CPU的使用情況。

  • CPU時間 = user + system + nice + idle + iowait + irq + softirq
  • CPU利用率 = 1 - (idle) / CPU時間。用于測量當前系統(tǒng)的 CPU負載情況。
  • CPU用戶態(tài)利用率 = (user+nice) / CPU時間。用于測量 用戶程序CPU的占用率。
  • CPU內(nèi)核態(tài)利用率 = (system) / CPU時間。如果程序有大量的 系統(tǒng)調(diào)用,導致 Linux內(nèi)核 占用了大量的 CPU,可以使用該數(shù)值來測量。
  • IO利用率 = iowait / CPU時間,測量 FLASH、內(nèi)存 等存儲介質(zhì)的 交互和等待時間。

3.1.1.3. /proc/loadavg

運行結(jié)果.png

loadavg 主要檢查當前系統(tǒng)的 負載情況:下面 按從左到右 逐個說明:

  1. 1分鐘 的平均負載
  2. 5分鐘 的平均負載
  3. 15分鐘 的平均負載
  4. 在采樣時刻,運行隊列的任務(wù)的數(shù)目,與 /proc/statprocs_running 表示相同意思
  5. 在采樣時刻,系統(tǒng)中 活躍的任務(wù)個數(shù)(不包括運行已經(jīng)結(jié)束的任務(wù))
  6. 最大的 pid值,包括 輕量級進程(即線程)。

更多關(guān)于 CPU負載 的講述可以參考 3.2.1.2小節(jié)。

3.1.2 proc進程相關(guān)

3.1.2.1 proc進程相關(guān)屬性

要查看 進程狀態(tài) ,先需要進入 進程proc目錄 下的文件夾,然后使用 cat stat 查看其狀態(tài),
如圖:

進程stat

下面按順序逐個介紹各個數(shù)據(jù)的意義:

  • pid:進程號(包括線程) ,在圖中值為 89
  • comm應(yīng)用程序 的名字,在圖中值為 sshd。
  • task_state:任務(wù)的狀態(tài),,在圖中值為 S。其各個狀態(tài)如下:
    1. R:runnign,即 運行態(tài)
    2. S:sleeping(TASK_INTERRUPTIBLE),即 睡眠態(tài)(可被打斷)
    3. D:deep sleep(TASK_UNINTERRUPTIBLE),即 睡眠態(tài)(不可被打斷)
    4. T:stopped,即 停止態(tài)
    5. t:tracing stop,即 暫停態(tài)(可被繼續(xù))
    6. Z:zombie,即僵尸態(tài)
    7. X:dead,即死亡態(tài)
  • ppid父進程ID,在圖中值為 1
  • pgid線程組號,在圖中值為 89。
  • sid:該任務(wù)所在的 會話組ID,在圖中值為 89
  • tty_nr:該任務(wù)的 tty終端設(shè)備號,在圖中值為 0。
  • tpgid:終端的 進程組號,即當前運行在該任務(wù)所在終端的前臺任務(wù) (包括shell及應(yīng)用程序) 的 PID,在圖中值為 -1。
  • task_flags:進程標志位,查看該任務(wù)的特性,在圖中值為 4194624。
  • min_flt:該任務(wù)不需要從硬盤拷數(shù)據(jù)而發(fā)生的 次缺頁次數(shù),在圖中值為 107
  • cmin_flt:該任務(wù)的累計所有的 waited-for進程 曾經(jīng)發(fā)生的 次缺頁次數(shù),在圖中值為 364。
  • maj_flt:該任務(wù)需要從硬盤拷數(shù)據(jù)而發(fā)生的 主缺頁次數(shù) ,在圖中值為 0
  • cmaj_flt:該任務(wù)累計的所有的 waited-for進程 曾經(jīng)發(fā)生的 主缺頁次數(shù),在圖中值為 0。
  • utime:該任務(wù)在 用戶態(tài) 運行的時間(單位為jiffies),在圖中值為 0。
  • stime:該任務(wù)在 核心態(tài) 運行的時間(單位為jiffies),在圖中值為 0。
  • cutime:該任務(wù)累計所有的 waited-for進程 曾經(jīng)在 用戶態(tài) 運行的時間(單位為jiffies),在圖中值為 12。
  • cstime:該任務(wù)累計所有的 waited-for進程 曾經(jīng)在 核心態(tài) 運行時間(單位為jiffies),在圖中值為 7。
  • priority:任務(wù)的 動態(tài)優(yōu)先級,在圖中值為 20
  • nice:任務(wù)的 靜態(tài)優(yōu)先級,在圖中值為 0。
  • num_threads:該任務(wù)所在的 線程組里的 線程個數(shù),在圖中值為 1。
  • it_real_value:由于 計時間隔 導致的下一個 SIGALRM 發(fā)送進程的時延(單位為jiffies), 在圖中值為 0
  • start_time:在系統(tǒng)啟動后,到與該任務(wù)啟動時的間隔(單位為jiffies), 在圖中值為 247。
  • vsize:該任務(wù)的 虛擬地址空間大小,在圖中值為 4268032。
  • rss:該任務(wù)當前 駐留物理地址空間的大小,這些頁可能用于 代碼、數(shù)據(jù)和棧 。在圖中值為 416。
  • rlim:該任務(wù)能駐留物理地址空間的 最大值 (單位為byte),在圖中值為 4294967295。
  • start_code:該任務(wù)在虛擬地址空間的 代碼段 的起始地址,在圖中值為 4648960。
  • end_code:該任務(wù)在虛擬地址空間的 代碼段的結(jié)束地址,在圖中值為 5267368。
  • start_stack:該任務(wù)在虛擬地址空間的 棧的結(jié)束地址,在圖中值為 3198615200
  • kstkespsp指針(堆棧指針) 的當前值, 與在進程的內(nèi)核堆棧頁得到的一致,在圖中值為 0。
  • kstkeipip指針(指令指針)的當前值,指向?qū)⒁獔?zhí)行的 指令指針,在圖中值為 0。
  • pendingsig待處理信號 的位圖,記錄發(fā)送給進程的普通信號,在圖中值為 0。
  • blocksig阻塞信號 的位圖 ,在圖中值為 0
  • sigignore忽略信號 的位圖,在圖中值為 4096
  • sigcatch被俘獲信號 的位圖 ,在圖中值為 81925。
  • wchan:如果該進程是 睡眠狀態(tài),該值給出 調(diào)度的調(diào)用點 ,在圖中值為 1。
  • nswap:被 swapped 的頁數(shù) (當前沒用) ,在圖中值為 0。
  • cnswap:所有子進程被 swapped 的頁數(shù)的 (當前沒用),在圖中值為 0。
  • exit_signal:該進程結(jié)束時,向父進程所發(fā)送的信號,在圖中值為 17。
  • processor:運行的 CPU編號 ,在圖中值為 0。
  • rt_priority實時進程相對優(yōu)先級別,在圖中值為 0
  • task_policy:進程的調(diào)度策略,其各個值如下,在圖中值為 0
    1. 0非實時進程
    2. 1FIFO實時進程
    3. 2RR實時進程
  • delayacct_blkio_ticks:累計的 塊I/O延遲,以 clock tick 為單位。在圖中值為 0
  • guest_time:為虛擬操作系統(tǒng)運行虛擬CPU所花費的時間,以 clock tick 為單位。在圖中值為 0。
  • cguest_time:進程的 子進程 用于用戶操作系統(tǒng)的時間,以 clock tick 為單位。在圖中值為0。
  • start_data:進程的 數(shù)據(jù)段BSS段起始地址。在圖中值為 5335032。
  • end_data:進程的 數(shù)據(jù)段BSS段結(jié)束地址。在圖中值為 5342208
  • start_brk:進程的 起始地址。在圖中值為 5365760。
  • arg_start:進程存放 命令行參數(shù)起始地址。在圖中值為 3198615427
  • arg_end:進程存放 命令行參數(shù)結(jié)束地址。在圖中值為 3198615442
  • env_start:進程存放 環(huán)境變量起始地址。在圖中值為 3198615442。
  • env_end:進程存放 環(huán)境變量結(jié)束地址。在圖中值為 3198615533。
  • exit_code:進程的 退出碼。在圖中值為 0

3.1.2.2 使用proc進程相關(guān)屬性計算CPU占有率

先看看下面的公式:

  • 進程CPU占用率 = 進程占用CPU時間 / 系統(tǒng)總的時間。
  • 進程占用 CPU 時間:可以從 進程的stat文件 獲得,包括 utime、stime、cutime、cstime
  • 系統(tǒng)總的時間:可以通過 /proc/stat 或者 gettime函數(shù) 獲得。

想要計算 CPU占有率 還對 進程的運行采樣,一般需要 2個采樣點

  • 采樣點1
    1. 系統(tǒng)時間記為 sys1
    2. 進程時間分別記為:utime1、stime1、cutime1、cstime1
  • 采樣點2
    1. 系統(tǒng)時間記為 sys2
    2. 進程時間分別記為:utime2、stime2、cutime2、cstime2

經(jīng)過計算和采樣后可以按照下面的公式來計算:

  • 進程CPU占用率 = ((utime2+stime2-cutime2-cstime2)-(utime1+stime1-cutime1-cstime1)) / (sys2-sys1)
  • 進程用戶態(tài)占用率 = ((utime2-cutime2)-(utime1-cutime1)) / (sys2-sys1)
  • 進程內(nèi)核態(tài)占用率 = ((stime2-cstime2)-(stime1-cstime1)) / (sys2-sys1)

3.2 系統(tǒng)性能評估工具

3.2.1 top

top 是一個常用的 性能分析 軟件,運行結(jié)果如下圖所示:

運行結(jié)果

下面將 top 分 2 點來講述:

  • 內(nèi)存分析
  • CPU及負載分析

3.2.1.1 內(nèi)存分析

運行結(jié)果如下圖所示:

內(nèi)存

可以看到它統(tǒng)計的內(nèi)存有:used、free、shrd、buff、cached
各個內(nèi)存的含義在前面的 內(nèi)存分析 文章中已經(jīng)說過,這里不再贅述

3.2.1.2 CPU及負載分析

1. CPU負載

Load AverageCPU負載 ,該數(shù)據(jù)是 每隔5秒鐘 檢查一次活躍的進程數(shù),然后按特定算法計算出的數(shù)值。

Load Average 可以理解為 CPU 的帶載情況,即有多少進程需要 CPU 來處理。它并不是描述 CPU的使用情況,其本質(zhì)應(yīng)該是 在單位時間內(nèi),CPU正在處理的進程數(shù)以及等待CPU處理的進程數(shù)之間的和,也就是CPU進程隊列的統(tǒng)計信息。

Load Average 越高說明越多的進程在搶占CPU,因而會導致 CPU資源的競爭越來越激烈,對于CPU資源的申請和維護的成本也會加大

理想情況下是一個CPU帶一個進程,這樣就不會發(fā)生CPU資源搶占。所以在一般情況下,如果這個數(shù)除以邏輯CPU的數(shù)量,結(jié)果高于5的時候就表明系統(tǒng)在超負荷運轉(zhuǎn)了。

CPU利用率和CPU負載的區(qū)別

  • CPU利用率指的是 程序在運行期間實時占用的CPU百分比
  • CPU負載指的是 一段時間內(nèi)正在使用和等待使用CPU的平均任務(wù)數(shù)。

兩者之間并沒有太大的關(guān)聯(lián)。比如一個進程一直在使用CPU進行運算,那么此時CPU利用率會達到100%,但平均負載則趨近于 1。反過來說,當CPU的工作負載越大,代表CPU必須要在不同的工作之間進行頻繁的工作切換。

按照筆者的理解:

  • CPU密集型進程容易讓CPU利用率升高
  • I/O 密集型進程容易導致平均負載升高
  • 頻繁調(diào)度進程容易讓平均負載和CPU利用率升高
2. CPU利用率及負載
CPU及負載
  • usr用戶空間 占用CPU的百分比。
  • sy內(nèi)核空間 占用CPU的百分比。
  • nic改變過優(yōu)先級的進程占用CPU的百分比
  • id空閑 CPU百分比
  • ioIO等待 占用CPU的百分比
  • irq硬中斷 占用CPU的百分比
  • sirq軟中斷 占用CPU的百分比
  • Load average:指 CPU的 負載均衡,后面的三個數(shù)分別是 1分鐘5分鐘、15分鐘的負載情況。

2.2.1.3 進程分析

image.png
  • PID進程ID
  • PPID父進程ID
  • USER:進程所有者
  • STAT:當前進程的狀態(tài),其值可以參考 進程stattask_state
  • VSZ:進程的 虛擬大小
  • %CPU:上次更新到現(xiàn)在的CPU時間占用百分比
  • COMMAND:運行的 命令行

3.2.2 vmstat

在嵌入式設(shè)備上,busybox 是不帶有該工具,而該工具的源碼也不是獨立的,而是在 procps 這個中聚集中。關(guān)于 procps 的交叉編譯請參考附錄中的:

  • json-c 交叉編譯(undefined reference to rpl_malloc )
  • 交叉編譯Procps-ng-3.3.11

編譯完成后將相應(yīng)的文件拷貝到設(shè)備端上就可以使用 vmstat 工具了,運行結(jié)果如下:

運行結(jié)果

該命令的意思是:每隔1秒運行1次vmstat,一共運行5次。

數(shù)據(jù)含義如下:

  • procs:系統(tǒng)中的進程狀態(tài)

    • r(running)運行隊列中的進程數(shù)量。
    • b(block)在阻塞等待IO的進程數(shù)量。
  • memory:系統(tǒng)的內(nèi)存使用情況

    • swpd使用的虛擬內(nèi)存 大小。如果 swpd 的值不為0且 SI,SO的值長期為0,這種情況不會影響系統(tǒng)性能。
    • free空閑物理內(nèi)存 大小。
    • buff:用作 文件緩沖 的內(nèi)存大小。
    • cache:用作 緩存 的內(nèi)存大小,如果cache值大,說明緩存的文件數(shù)多,如果頻繁訪問到的文件都能被cache中,那么磁盤的 讀IO(bi) 會非常小。
  • swap:內(nèi)存交換情況

    • si每秒從交換區(qū)寫到內(nèi)存的大小,由磁盤調(diào)入內(nèi)存
    • so每秒寫入交換區(qū)的內(nèi)存大小,由內(nèi)存調(diào)入磁盤。

注意: 內(nèi)存夠用的時候,這2個值都是 0。如果這2個值長期大于0時,系統(tǒng)性能會受到影響,磁盤IOCPU資源 都會被消耗。不能因為觀察到 空閑內(nèi)存(free) 很少或接近于0時,就認為內(nèi)存不夠用了。還需要結(jié)合 siso。如果 閑內(nèi)存(free)很少,同時siso 也很少(大多時候是0),那么此時說明內(nèi)存剛好夠用,系統(tǒng)性能暫時不會受到影響的。

  • IO:系統(tǒng)IO的使用情況,如果值越大
    • bi每秒讀取 的塊數(shù)
    • bo每秒寫入 的塊數(shù)

注意:隨機磁盤讀寫的時候,bi** 和 bo 值越大,能看到 CPUIO等待(wa) 也會越大。**

  • system:系統(tǒng)情況
    • in每秒中斷數(shù),包括時鐘中斷。
    • cs每秒上下文切換數(shù)。

注意: 隨機磁盤讀寫的時候,incs 值越大,能看到 CPU內(nèi)核消耗的CPU時間(sy) 也會越大。

CPUCPU 的使用情況,以 百分比表示

  • us用戶進程執(zhí)行時間百分比(us) 的值比較高時,說明 用戶進程消耗的CPU時間多。如果該值長期超 50%,那么我們就該考慮對程序進行優(yōu)化。
  • sy: 內(nèi)核系統(tǒng)進程執(zhí)行時間百分比(sy) 的值高時,說明 系統(tǒng)內(nèi)核消耗的CPU資源多。此時系統(tǒng)的情況比較糟糕,我們需要排查原因并優(yōu)化
  • waIO等待時間百分比(wa) 的值高時,說明 IO等待 比較嚴重。這可能由于 磁盤大量作隨機訪問造成,也有可能 磁盤出現(xiàn)瓶頸(塊操作)。
  • id空閑時間百分比

四、查找性能瓶頸

在分析程序的時候,往往需要找個程序的 性能瓶頸。從而對 性能瓶頸 進行優(yōu)化,這樣可以取得比較大的收益。我們一般都使用功能來查找 性能瓶頸,一般有 gprofOProfile 或者 perf 等。本文著重說明一下 perf 的使用方法,及如何將 perf 獲取到的數(shù)據(jù)轉(zhuǎn)換為 火焰圖 來分析程序性能。

4.1 perf

4.1.1 perf編譯

perf 依賴于幾種庫,分別如下:

  • zlib
  • elfutils
  • binutils
    其中 binutils 就是我們常用的 readelf、ar 等編譯器工具,一般情況下我們的 交叉編譯鏈 都有自帶,所以我們需要自行編譯 zlibelfutils

1. zlib交叉編譯

  1. 點擊 zlib下載地址 下載 zlib 源碼。
  2. 解壓并進入 zlib的目錄
  3. 使用命令導出 交叉編譯工具,命令舉例如下:

export CC=arm-linux-gnueabihf-gcc

  1. 使用命令進行配置,該命令會直接將編譯出來的庫自動加入到我們的 庫路徑,我們就不再需要去指定 庫路徑 了。如下:

./configure --host=arm-linux-gnueabihf --prefix=/your_compiler_path/arm-linux-gnueabihf/libc/usr

附錄

  1. 編譯安裝

make -j12 && make install

2. elfutils交叉編譯

我們編譯 elfutils 是需要其里面的庫 libelf。編譯過程依賴 2 個工具 m4bison。在編譯前請檢查編譯機是否有安裝該工具。編譯步驟如下:

  1. 點擊 elfutils下載地址 下載 elfutils 源碼。
  2. 解壓并進入目錄
  3. 使用命令進行配置:

./configure --host=arm-linux-gnueabihf --prefix=/your_compiler_path/arm-linux-gnueabihf/libc/usr/ --disable-debuginfod

  1. 編譯安裝

make -j12 && make install

3. perf交叉編譯

perf源碼 在我們開發(fā)板上 linux 的源碼中,其路徑一般是 /your_linux_path/tool/perf
編譯步驟如下:

  1. 進入 perf 源碼目錄
  2. 使用命令進行編譯

make LDFLAGS=-static ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- DEBUG=1 HAVE_CPLUS_DEMANGLE=1

  1. 編譯完成后在 perf目錄下 有個叫 perf 的執(zhí)行文件。將 perf、libzlibelf 拷貝到開發(fā)板上對應(yīng)的位置,就是 庫路徑和執(zhí)行文件路徑

4.1.2 perf概念

perf 的原理是通過對 系統(tǒng)事件 進行 統(tǒng)計采樣 得到 統(tǒng)計數(shù)據(jù),再通過分析 統(tǒng)計數(shù)據(jù) 得出性能瓶頸的結(jié)果。

perf 中能采樣 系統(tǒng)事件 分為以下 3 類:

  • Hardware Event:由 PMU部件或芯片硬件如cache等 產(chǎn)生,在特定的條件下探測 硬件系統(tǒng)事件是否發(fā)生以及發(fā)生的次數(shù)。
  • Software Event:是內(nèi)核產(chǎn)生的事件,分布在各個功能模塊中,統(tǒng)計和操作系統(tǒng)相關(guān)性能事件。比如進程切換,clock_tick數(shù)等。
  • Tracepoint Eventtracepoints 是散落在內(nèi)核源碼中的一些 hook函數(shù),它們可以在特定的代碼被執(zhí)行到時觸發(fā),比如 slab分配器的分配次數(shù)。根據(jù)一特性,perftracepoints 產(chǎn)生的時間記錄下來,生成報告,通過分析這些報告對性能做出準確的判斷。

4.1.2 perf使用方法

perf 是一個工具集,一共包含 20多種 工具,下面看看常用的 5種工具。

1. perf list

perf 的運行原理就是對 系統(tǒng)事件 進行采樣,從而得到相關(guān)數(shù)據(jù)來分析。我們可以使用perf list 來查看編譯出來的 perf 支持哪些 系統(tǒng)事件。每個 linux版本perf 可能有所不同。筆者的運行結(jié)果如下:

運行結(jié)果

可以看到一共有 10種軟件事件3種硬件事件。筆者編譯出來的 peff 比較簡陋,所以本文主要講述 軟件事件 相關(guān)的分析。

2. perf stat

perf stat 可以統(tǒng)計 程序整體情況,它能夠顯示出 程序系統(tǒng)事件 統(tǒng)計數(shù)據(jù)。一般用于 分析程序的整體性能

常用選項如下

  • -a(--all-cpus):顯示 所有CPU上的統(tǒng)計信息。
  • -C(--cpu <cpu>):顯示 指定CPU的統(tǒng)計信息。
  • -D(--delay <n>):指定 命令的延遲統(tǒng)計時間,單位為 毫秒
  • -d(--detailed):顯示更多細節(jié)信息。
  • -e(--event <event>):統(tǒng)計 指定事件 的數(shù)據(jù)。
  • -o(--output <file>)輸出統(tǒng)計信息到文件。
  • -p(--pid <pid>):對指定 pid的進程 進行統(tǒng)計。
  • -t(--tid <tid>):對指定 tid的線程 進行統(tǒng)計。
  • -r(--repeat <n>):重復命令 n次 并顯示 平均結(jié)果
  • -S(--sync):在開始執(zhí)行前使用 sync函數(shù)。

運行結(jié)果如下:


image.png

perf stat 顯示的默認 統(tǒng)計信息如下:

  • task-clock-msecs:指 CPU利用率。該值越高,說明程序花費越多的時間在 CPU計算 上而不 文件IO。
  • context-switches:指 進程切換次數(shù),記錄了程序運行過程中發(fā)生 進程切換 的次數(shù)。應(yīng)當盡量避免頻繁的進程切換。
  • cache-misses:指 **程序運行過程中的 cache非命中次數(shù)。如果該值越高,說明程序的 cache命中率越低
  • cache-references:指 **程序運行過程中的 cache命中次數(shù)。如果該值越高,說明程序的 cache命中率越高
  • CPU-migrations:表示 進程 在運行過程中發(fā)生的 CPU遷移次數(shù)
  • cycles處理器時鐘周期,一條機器指令可能需要多個 cycles。
  • instructions:指 程序的機器指令數(shù)目。
  • IPC:指 instructions/cycles 。該值越大,說明 程序越充分利用了處理器

3. perf top

perf toptop 不同的是。top 常用于分析系統(tǒng)的整體性能。而 perf top 可以精確到系統(tǒng)或程序中 函數(shù)級的運行情況

常用選項如下

  • -e <event>:指明要分析的性能事件。
  • -p <pid>:僅分析 pid目標進程及其創(chuàng)建的線程。
  • -k <path>:帶符號表的內(nèi)核映像所在的路徑,用于注釋函數(shù)。
  • -K:不顯示屬于內(nèi)核或模塊的符號。
  • -U:不顯示屬于用戶態(tài)程序的符號。
  • -d <n>:界面的 刷新周期,默認為 2s。因為 perf top 默認 每2smmap 的內(nèi)存區(qū)域讀取一次性能數(shù)據(jù)。
  • --call-graph fractal:路徑上的調(diào)用率為 相對值,加起來為100%。調(diào)用順序為 從下往上
  • perf top --call-graph graph:路徑調(diào)用率為 絕對值,加起來為該函數(shù)的 熱度。

運行結(jié)果如下:


image.png
  • 第一列:符號引發(fā)的 性能事件的比例,默認指 占用的cpu周期比例
  • 第二列:符號所在的 DSO(Dynamic Shared Object),可以是 應(yīng)用程序、內(nèi)核、動態(tài)鏈接庫、模塊。
  • 第三列DSO的類型。
    [.]:表示此符號屬于 用戶態(tài)的ELF文件,包括 8可執(zhí)行文件與動態(tài)鏈接庫*。
    [k]:表述此符號屬于內(nèi)核或模塊。
  • 第四列符號名。如果有些符號 不能解析為函數(shù)名,只能用 地址 表示。

4. perf record

perf record 可以 用于記錄perf統(tǒng)計的數(shù)據(jù) 并輸出到 文件perf.data,并使用相關(guān)工具生成 可視化 圖像來分析程序。配合 perf report 可以精確的分析程序

常用選項:

  • -e:指定記錄的 系統(tǒng)事件
  • -a:指定記錄 所有CPU的事件
  • -p:記錄指定 pid進程的事件
  • -o:指定 保存數(shù)據(jù)的文件名
  • -g:指定 生成函數(shù)調(diào)用圖
  • -C:記錄 指定CPU的事件
  • -F:設(shè)置統(tǒng)計頻率

這里不展開說 perf report,我們使用另一個強有力的工具 火焰圖FlameGraph,可以在 github 上找到火焰圖的項目,點擊下載火焰圖生成軟件

下面我們通過一個例程來說明:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>

void* func_test1(void* arg)
{
    int j = 0;
    for(int i = 0; i < 90000000; i++) 
        j=i; 
    return NULL;
}

void* func_test2(void* arg)
{
    int j = 0;
    for(int i = 0; i < 120000000; i++) 
        j=i; 
    return NULL;        
}

int main()
{
    pthread_t test1 = 0;
    pthread_t test2 = 0;

    pthread_create(&test1, NULL, func_test1, NULL);
    pthread_create(&test2, NULL, func_test2, NULL);
    pthread_join(test1,NULL);
    pthread_join(test2,NULL);

    return 0;
}

注意,在使用時需要注意要加參數(shù) -g。
步驟如下:

  1. 在設(shè)備上運行 perf record -a -g your_program
  2. 使用命令 perf script -i perf.data > perf.unfold 將生成的 perf.data 轉(zhuǎn)換為 perf.unfold
  3. 將生成的 perf.unfold 拷貝到宿主機上
  4. 使用 FlameGraph 中的工具執(zhí)行命令 ./stackcollapse-perf.pl perf.unfold > perf.fold
  5. 使用命令 ./flamegraph.pl perf.fold > perf.svg 生成 svg文件
  6. 使用 瀏覽器 打開 svg文件 (筆者使用的是chrom)

結(jié)果如下:


運行結(jié)果

可以看到 func_test1func_test2 占據(jù)了 執(zhí)行時間 的大頭,在旁邊還有 2個 函數(shù)在執(zhí)行。
點擊 左側(cè)紅框 查看如下:

左側(cè)

從結(jié)果中可以看到是 perf 自身的調(diào)用情況。

再點擊 右側(cè)紅框 查看如下:

右側(cè)

可以看到是一個名為 swapper 的函數(shù)在執(zhí)行,而這個函數(shù)是干什么用的呢?
其實在 CPU 執(zhí)行程序時,空閑的時候就會調(diào)用 swapper 函數(shù),此時 CPU 處于 idle狀態(tài)。

五、程序優(yōu)化

前面我們講了 程序優(yōu)化 的幾個層次,分別是:

  1. 算法和數(shù)據(jù)結(jié)構(gòu)的優(yōu)化
  2. 編譯器優(yōu)化
  3. 代碼優(yōu)化
  4. 硬加速

其中 算法和數(shù)據(jù)結(jié)構(gòu)的優(yōu)化 不在本文內(nèi)容計劃之內(nèi)。使用 SIMD指令硬加速 已經(jīng)在筆者的其他博客 neon指令 系列一將說到。 下面主要簡單地講述一下 編譯器優(yōu)化代碼優(yōu)化。

5.1 編譯器優(yōu)化

編譯器按照 arm-linux-gcc 系列來說明。下面將按書中所寫,列舉出 gcc 的相關(guān)編譯優(yōu)化選項。如果你想 關(guān)掉 某一個優(yōu)化選項, 你可以在 -f優(yōu)化選項 之間增加 no。
在編譯時,我們一般回加入 -O1、-O2和-O3 等選項,其實它們的作用就是讓編譯器進行不同程度的優(yōu)化,那么每種優(yōu)化打開的編譯選項都不同,下面看看每種優(yōu)化都打開了哪些選項。

5.1.1 -O1優(yōu)化

  • -fcprop-registers:
    復寫傳播,一般情況下該選項會與 常量折疊 一起出現(xiàn)。因為在函數(shù)中把寄存器分配給變量,所以編譯器執(zhí)行二次檢查以便 減少調(diào)度依賴性 并且 刪除不必要的寄存器復制操作。以下面的代碼作說明。
const int i = 2*2;

編譯器確實將 2*2 算成 4 了,以后碰到 i 就用 4 替換,這個計算 2*2 的過程據(jù)說叫 常量折疊(const folding),而用 4 替換 i 的過程叫做 復寫傳播(copy propagation)。

  • -fdefer-pop:
    該優(yōu)化選項與 函數(shù)返回 有關(guān)。 一般情況下,函數(shù)返回,輸入?yún)?shù)被立即彈出堆棧后。但該優(yōu)化選項會 推遲彈出函數(shù)調(diào)用的輸入?yún)?shù),等必要時與幾個函數(shù)調(diào)用參數(shù)一起彈出。這樣可以 節(jié)省處理時間,但也 會使堆棧中的內(nèi)容有些雜亂。

  • -fdelayed-branch:
    該選項會讓編譯器 試圖根據(jù)指令周期時間重新安排指令, 把盡可能多的指令 移動到條件分支前,以便充分的利用處理器的 緩存。

  • -fguess-branch-probability:
    該選項直譯為 猜測分支可能性,該選項試圖讓編譯器 確定條件分支可能的結(jié)果,并且移動相應(yīng)的指令。這有可能導致不同的編譯器會編譯出迥然不同的目標代碼。

  • -fif-conversion:
    if-then 語句應(yīng)該是程序中僅次于循環(huán)的最消耗時間的語句。簡單的if-then語句可能在最終的 匯編代碼 中產(chǎn)生 較多條件分支。 通過 減少或者刪除條件分支 ,以及使用 條件傳送、設(shè)置標志運算技巧 來替換 if-then語句。由此編譯器可以減少 if-then語句 花費的時間量。

  • -fif-conversion2:
    相比于 -fif-conversion-fif-conversion2 加入更高級的 數(shù)學特性, 減少實現(xiàn) if-then語句 所需的 條件分支

  • -floop-optimize:
    優(yōu)化循環(huán) 通??梢院艽蟪潭鹊?提高程序性能。一般情況下,程序會存在 大型且復雜的循環(huán)。通過刪除在循環(huán)內(nèi)沒有改變值的變量賦值操作,可以 減少循環(huán)內(nèi)執(zhí)行指令的數(shù)量,在很大程度上提高性能。 同時也優(yōu)化那些確定何時離開循環(huán)的條件分支, 以便減少分支的影響。

  • -fmerge-constants:
    嘗試(string constants and floating point constants)
    該選項會讓編譯器試圖 橫跨編譯單元合并同樣的常量。這一特性有時候會導致 很長的編譯時間。

5.1.2 -O2優(yōu)化

  • -falign-functions:
    這個選項用于使 函數(shù)對準內(nèi)存中特定邊界的開始位置。大多數(shù)處理器按照頁面讀取內(nèi)存,并且確保全部函數(shù)代碼位于單一內(nèi)存頁面內(nèi)。這樣 同一函數(shù)就不需要讀取到多個內(nèi)存頁中。

  • -fcaller-saves:
    該選項可以讓編譯器對 函數(shù)調(diào)用 只進行一次 寄存器的保存和恢復操作,而不是在每個函數(shù)調(diào)用中都進行。 如果調(diào)用多個函數(shù), 這樣能夠節(jié)省時間。

  • -fcrossjumping:
    該選項讓編譯器 跨越跳轉(zhuǎn)的轉(zhuǎn)換代碼進行處理, 以便組合分散在程序各處的相同代碼。 這樣可以 減少代碼的長度, 但是有可能不會對程序性能產(chǎn)生直接影響。

  • -fcse-follow-jumps:
    CES即通用子表達式消除技術(shù)(common subexpression elimination)。在程序中,有時候會有一些 無法到達的代碼,該選項讓編譯器查找程序中的此類代碼,然后跳過該代碼的 跳轉(zhuǎn)指令。最常見的就是 if-elseelse部分

  • -fdelete-null-pointer-checks:
    該選項讓編譯器掃描 匯編語言代碼,檢查可能存在 空指針的代碼。 編譯器假設(shè)間接引用空指針將 停止程序

  • -fexpensive-optimizations:
    該選項讓編譯器執(zhí)行 代價高昂的各種優(yōu)化技術(shù)(編譯時的角度)。但是不一定保證運行時性能能提升,有可能對運行時的性能產(chǎn)生負面影響。

  • -fforce-mem:
    該選項讓編譯器在做算術(shù)操作前,強制將內(nèi)存數(shù)據(jù)copy到寄存器 中以后再執(zhí)行。對于只涉及 單一指令的變量來說,這樣也許不會有很大的優(yōu)化效果。但是對于 很多指令中都涉及到的變量(比如數(shù)學操作) 來說,這時有顯著的優(yōu)化。因為和訪問內(nèi)存中的值相比,處理器訪問寄存器中的值要快的多。
    該選項有可能導致 內(nèi)存與寄存器之間的數(shù)據(jù)不一致。對于某些依賴 內(nèi)存操作順序而進行的邏輯,需要做 嚴格的處理 后才能進行優(yōu)化。例如采用 volatile關(guān)鍵字限制變量的操作方式或者 利用barrier迫使cpu嚴格按照指令序執(zhí)行。

  • -fgcse:
    該選項讓編譯器 所有匯編代碼執(zhí)行全局通用表達式消除。該操作 試圖分析匯編代碼并且結(jié)合通用片段,消除冗余的代碼段。如果代碼使用 計算性的goto,推薦 使用-fno-gcse選項關(guān)閉該優(yōu)化選項。

  • -foptimize-sibling-calls:
    該選項可以優(yōu)化 尾遞歸的調(diào)用。一般情況下 遞歸的函數(shù) 可以被 展開為一系列一般的指令, 而不是使用分支。 這樣處理器的指令緩存能夠加載展開的指令并且處理他們。和 需要分支操作的函數(shù)相比這樣更快。

  • -fregmove:
    該選項讓編譯器試圖 重新分配 mov 指令中使用的寄存器,并且將其作為 其他指令的操作數(shù),以便 最大化捆綁的寄存器的數(shù)量。說實在的,筆者目前也不清楚書中所講關(guān)于該選項的內(nèi)容,有知道的讀者還請不吝賜教

  • -freorder-functions:
    該選項讓編譯器重新安排指令塊以便改進 分支操作代碼局部性。

  • -frerun-cse-after-loop:
    該選項讓編譯器在對 任何循環(huán) 已經(jīng)進行過優(yōu)化之后執(zhí)行 運行通用子表達式消除。這樣確保在展開循環(huán)代碼之后更進一步地優(yōu)化代碼

  • -fsched-interblock:
    該選項讓編譯器能夠 跨越指令塊調(diào)度指令。 這可以非常靈活地 移動指令以便等待期間完成的工作最大化。

  • -fschedule-insns:
    該選項讓編譯器將 重新安排指令,以讓等待數(shù)據(jù)的處理器可以執(zhí)行其他操作。 比如對于在進行浮點運算時有延遲的處理器來說, 這使處理器在等待浮點結(jié)果時可以加載其他指令

  • -fstrength-reduce:
    該選項讓編譯器對循環(huán)執(zhí)行優(yōu)化并且 刪除迭代變量。迭代變量 是 捆綁到循環(huán)計數(shù)器的變量,類似于for循環(huán)中常用的 i,j。

  • -fstrict-aliasing:
    該選項讓編譯器強制實行高級語言的嚴格變量規(guī)則,確保不在數(shù)據(jù)類型之間共享變量。比如 intfloat 不使用相同的內(nèi)存區(qū)域。

  • -fthread-jumps:
    在某些情況下, 一條跳轉(zhuǎn)指令可能轉(zhuǎn)移到另一條分支語句。 通過一連串跳轉(zhuǎn),編譯器確定多個跳轉(zhuǎn)之間的終目 標并且把第一個跳轉(zhuǎn)重新定向到終目標。 也就是直接優(yōu)化從第一個跳轉(zhuǎn)到最后一個目標代碼

  • -funit-at-a-time:
    該選項讓編譯器在優(yōu)化程序之前讀取 整個匯編代碼。 這使編譯器可以 重新安排不消耗大量時間的代碼以便優(yōu)化指令緩存。 缺點是編譯時 花費相當多的內(nèi)存,對于 小型計算機可能是一個問題

5.1.3 -O3優(yōu)化

  • -fgcse-after-reload:
    完全重新加載生成的且優(yōu)化后的匯編語言代碼之后執(zhí)行第二次gcse優(yōu)化,幫助消除不同優(yōu)化代碼方式創(chuàng)建的任何冗余段.

  • -finline-functions:
    該選項讓編譯器 不為函數(shù)創(chuàng)建單獨的匯編語言代碼, 而是把函數(shù)代碼包含在調(diào)度程序的代碼中。按照筆者理解將多個函數(shù)集成到一個函數(shù)中。該優(yōu)化選項可用充分的利用指令緩存,而不是在每次函數(shù)調(diào)用時進行分支操作,由此提高性能。

5.2 代碼優(yōu)化

按照筆者理解,代碼優(yōu)化 更加考驗一個人的經(jīng)驗積累。本節(jié)不會詳細介紹書中每種 代碼優(yōu)化技巧 的原理,主要是總結(jié) 代碼優(yōu)化技巧結(jié)論??梢钥焖賾?yīng)用于 開發(fā) 中。

  • 數(shù)據(jù)類型:針對 8位和16位ARM 是在將數(shù)據(jù)加載到寄存器中,完成 符號位擴展。數(shù)據(jù)加載指令 ldr、ldrsb、ldrb、 lsdh、lsdsh、ldr等,它們的 指令周期是一樣的。所以 char、short、int 其效率是相同的。

  • if與switch:如果 case值 變化不大(太大的話,會導致跳轉(zhuǎn)表過大,造成內(nèi)存的浪費),有足夠的分支(大 于 4 個分支),switch 會帶來性能極大的提升。

  • 循環(huán)

    1. 展開循環(huán)分支,降低循環(huán)的次數(shù),減少 分支語句 對循環(huán)的影響。
    2. 合并循環(huán),既可以 提高程序性能并減少代碼尺寸,同時不影響功能。
    3. 流水線級數(shù)越高,對分支操作越加敏感。在程序正常運行期間處理當前代碼時,預(yù)取模塊讀取并解碼 下一部分指令而 不使存儲總線閑置下來。如果遇到 分支語句,則會 清除流水線而立刻使預(yù)先進行的讀取和解碼工作無效。流水線 級數(shù)越多 ,系統(tǒng)就花費越多的時間 來填充流水線。在這期間 CPU會閑置下來。因此,分支語句越少,性能越高。
  • 數(shù)組:盡量使用數(shù)組的大小是 4或8 的倍數(shù),用此倍數(shù)展開循環(huán)體 一維數(shù)組和多維數(shù)組 。

  • 函數(shù)

    1. 函數(shù)的參數(shù)最好 不多于 4 個。
    2. 盡量限制函數(shù)內(nèi)部循環(huán)所用局部變量的數(shù)目,最好 不超過12個,以便編譯器能把 變量分配到寄存器。
    3. 如果不需要 返回值,則盡量定義為 void,這樣可以 節(jié)省 一個 寄存器R0。 如果需要返回值,則最好 限定在4個字節(jié),使用一個 寄存器R0 即可。

六、參考鏈接

/proc/stat 詳解
Linux top命令詳解
Linux CPU性能優(yōu)化方法
json-c 交叉編譯(undefined reference to rpl_malloc )
交叉編譯Procps-ng-3.3.11【轉(zhuǎn)】
CPU 利用率背后的真相,你知道嗎?
CPU利用率與Load Average的區(qū)別?
死鎖一定會造成cpu使用率飆升嗎?
深入理解perf報告中的swapper進程
系統(tǒng)級性能分析工具perf的介紹與使用
perf工具+ 火焰圖分析性能
perf 工具原理與使用
GNU下優(yōu)化代碼
gcc -o 優(yōu)化選項

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 特別說明: 1、本文只是面對數(shù)據(jù)庫應(yīng)用開發(fā)的程序員,不適合專業(yè)DBA,DBA在數(shù)據(jù)庫性能優(yōu)化方面需要了解更多的知識...
    安易學車閱讀 2,141評論 0 40
  • 編寫和優(yōu)化Go代碼 本文檔概述了編寫高性能Go代碼的最佳實踐。 雖然有些討論會提高單個服務(wù)的速度(通過緩存等),但...
    freelang閱讀 2,325評論 0 4
  • 性能調(diào)優(yōu)工具如 perf,Oprofile 等的基本原理都是對被監(jiān)測對象進行采樣,最簡單的情形是根據(jù) tick 中...
    abeb6ca9bb86閱讀 10,871評論 2 15
  • 嗨~親愛的自己,你還好嗎? 現(xiàn)在的我已經(jīng)是一名妥妥的準大一新生了,還有一周就要去學校報道了呢。站在高中的終點和大學...
    林翊軒閱讀 2,503評論 1 3
  • 小小設(shè)計師閱讀 142評論 0 0

友情鏈接更多精彩內(nèi)容