【原創】CPU性能優化小記

CPU性能優化小記

一、現象

業務線反饋,單板只要一跑我們的通訊軟件appA,CPU就變得很高,即使沒有任何通訊,空跑時CPU利用率同樣的高,難得業務會關注CPU性能,好久沒更新博客了,小記一下(又是debug一兩天,最後改一行代碼解決的問題o(╯□╰)o)。

啓動appA 前,top看一下CPU使用情況,如下,9%還算正常(有其他業務應用)。

Mem: 49320K used, 202284K free, 0K shrd, 0K buff, 27420K cached
CPU:  3.0% usr  6.0% sys  0.0% nic 91.0% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 1.79 2.79 2.98 1/86 1721
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
 1721  1376 root     R     2272  0.9   0  1.6 top -d 1
  917     1 root     S    38936 15.4   0  1.1 ...
  908     1 root     S    38936 15.4   0  0.5 ...
  915     1 root     S    38936 15.4   0  0.5 ...
  896     1 root     S    38936 15.4   0  0.5 ...
  914     1 root     S    38936 15.4   0  0.5 ...
  857     1 root     S     4396  1.7   0  0.5 /usr/sbin/telnetd
    3     2 root     SW       0  0.0   0  0.5 [ksoftirqd/0]
 1376   857 root     S     2272  0.9   0  0.0 -sh
  929     1 root     S     2272  0.9   0  0.0 /sbin/getty -L ttyO2 115200 vt100
    1     0 root     S     2268  0.9   0  0.0 init
  ...

我們的應用啓動後,CPU使用率很高,65%左右,是什麼讓CPU佔用高?

$top -d1
Mem: 54604K used, 197000K free, 0K shrd, 0K buff, 27220K cached
CPU: 14.6% usr 38.5% sys  0.0% nic 36.2% idle  0.0% io  0.0% irq 10.5% sirq
Load average: 3.26 2.22 1.58 1/107 1470
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
 1444  1376 root     S    28008 11.1   0  5.8 {Thread_start} ./appA
    3     2 root     SW       0  0.0   0  4.0 [ksoftirqd/0]
 1445  1376 root     S    28008 11.1   0  2.9 {Task_OS_Timer} ./appA
 1470  1376 root     R     2272  0.9   0  2.3 top -d 1
  917     1 root     S    38936 15.4   0  1.7 ...
 1450  1376 root     S    28008 11.1   0  1.7 ...
  896     1 root     S    38936 15.4   0  1.1 ...
 1449  1376 root     S    28008 11.1   0  0.5 {CycleIO} ./appA
  857     1 root     S     4396  1.7   0  0.5 /usr/sbin/telnetd
  ......

TOP各指標含義

簡單介紹下top各指標含義。top 命令顯示了 us、sy、nic、idle 、io、hi、si 和 st 這幾個指標(不同版本的top顯示簡寫有所不同),各指標含義如下,這幾個指標之和爲 100。

us, user    : time running un-niced user processes
sy, system  : time running kernel processes
ni, nice    : time running niced user processes
id, idle    : time spent in the kernel idle handler
wa, IO-wait : time waiting for I/O completion
hi : time spent servicing hardware interrupts
si : time spent servicing software interrupts
st : time stolen from this vm by the hypervisor
CPU指標 含義
us CPU執行用戶代碼消耗的CPU時間比例,這個比例越高,說明CPU的利用率越好,因爲我們的目的就是爲了讓CPU將更多的時間用在執行用戶代碼上,如果這個指標過高影響了你的業務,那就需要去分析業務代碼了。
sy CPU執行內核代碼消耗的CPU時間比例,這個比例要越低越好,因爲CPU不應該把時間浪費在執行內核函數上,如內核函數浪費了很多時間,那說明內核可能需要優化了。
ni Nice值被調大的線程執行用戶態代碼消耗的CPU百分比。對於非實時任務,通過調整線程的Nice值可以控制它的CPU時間,當我們增大一個線程的Nice值時,我們期望它可以少佔用一些CPU時間,CPU利用率裏的ni這個值就給我們一個參考指標來觀察我們的調整效果。
運行一個進程後,它的默認Nice值爲0,通過renice來增加該進程的Nice值(正數),該進程的CPU利用率就會在ni中顯示,而減少進程的Nice值(負數)則不會在這裏面顯示。
id CPU空閒時間,我們通常說的整體CPU利用率就是(100-idl)這個值,也是其他幾項的和。爲了提升系統的資源利用率,目標就是儘量降低idl。
wa CPU阻塞在I/O上的時間,比如CPU從磁盤讀取文件內容,由於磁盤I/O太慢,導致CPU不得不等待數據就緒,然後才能繼續執行下一步操作,這就wai時間,這個值越高,說明I/O處理能力出現了問題。
hi CPU消耗在硬中斷裏的時間,正常情況下這個值都很低,因爲中斷的處理很快,即使有大量的硬件中斷,也不會消耗很多的CPU時間。
si CPU消耗在軟中斷裏的時間,系統中常見的軟中斷有網絡收發包軟中斷、寫文件落盤產生的軟中斷、高精度定時器軟中斷等。所以網絡吞吐量較高的系統中,這個值也會高一些。

在上面這幾項中,idle 和 wait 是 CPU 不工作的時間,其餘的項都是 CPU 工作的時間。idle 和 wait 的主要區別是,idle 是 CPU 無事可做,而 wait 則是 CPU 想做事卻做不了。你也可以將 wait 理解爲是一類特殊的 idle,即該 CPU 上有至少一個線程阻塞在 I/O 時的 idle。

二、分析

CPU: 14.6% usr 38.5% sys 0.0% nic 36.2% idle 0.0% io 0.0% irq 10.5% sirq可以看出,CPU 利用率飆高是由 sys 利用率和sirq利用率變高導致的,也就是說 sys 利用率會忽然飆高一下,比如在 usr 低於 15% 的情況下,sys 會高於 40%,同時sirq會高於10%,持續不到一秒後又恢復正常。

CPU 的 sys 利用率高,說明內核函數執行花費了太多的時間,同樣,CPU 的 sirq利用率高,說明內核處理軟中斷執行花費了太多的時間,先對比應用啓動前後系統各部分信息,看看能否推斷出CPU執行哪部分的的內核函數花費的時間長。

啓動應用前

我們先通過pidstat看看appA啓動前,系統中每個進程usr、system、guest、wait各部分CPU使用情況:

$ pidstat 1 3  #每秒打印一次 打印3次
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

09:45:44      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
09:45:44        0        47    0.00    0.96    0.00    0.00    0.96     0  kworker/0:1
09:45:44        0       894    2.88    3.85    0.00    0.00    6.73     0  cpu_mem_task
09:45:44        0       968    0.00    1.92    0.00    0.00    1.92     0  pidstat

09:45:44      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
09:45:45        0       857    0.00    1.00    0.00    0.00    1.00     0  telnetd
09:45:45        0       881    0.00    1.00    0.00    0.00    1.00     0  xxxx
09:45:45        0       894    2.00    5.00    0.00    0.00    7.00     0  xxxx
09:45:45        0       968    1.00    2.00    0.00    0.00    3.00     0  pidstat

09:45:45      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
09:45:46        0         3    0.00    1.00    0.00    0.00    1.00     0  ksoftirqd/0
09:45:46        0       894    1.00    6.00    0.00    0.00    7.00     0  xxxx
09:45:46        0       968    1.00    2.00    0.00    0.00    3.00     0  pidstat

可以看到,各進程正常,CPU使用率低,接下來通過mpstat 查看內核各部分詳細。

]# mpstat --help
Usage: mpstat [ options ] [ <interval> [ <count> ] ]
Options are:
[ -A ] [ -n ] [ -T ] [ -u ] [ -V ]
[ -I { SUM | CPU | SCPU | ALL } ] [ -N { <node_list> | ALL } ]
[ --dec={ 0 | 1 | 2 } ] [ -o JSON ] [ -P { <cpu_list> | ALL } ]
  • -A: 等同於-u -I ALL -P ALL
  • -I:指定SUM CPU SCPU ALL四個參數,SUM表示每個處理器的中斷總數,CPU表示每個核的每秒中斷數量, SCPU表示每個核每秒的軟中斷數量。
  • -P: 統計的CPU編號,一般用ALL
  • -u: 輸出列的信息
  • -V: 查看版本號
  1. CPU利用率

每秒輸出1次, 輸出5次:

[root@/tmp]# mpstat 1 5
09:49:51     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
09:49:52     all    1.01    0.00    5.05    0.00    0.00    2.02    0.00    0.00    0.00   91.92
09:49:53     all    2.15    0.00    1.08    0.00    0.00    0.00    0.00    0.00    0.00   96.77
09:49:54     all    0.00    0.00    0.00    0.00    0.00    1.09    0.00    0.00    0.00   98.91
09:49:55     all    2.04    0.00    3.06    0.00    0.00    2.04    0.00    0.00    0.00   92.86
09:49:56     all    0.00    0.00    2.15    0.00    0.00    0.00    0.00    0.00    0.00   97.85
Average:     all    1.05    0.00    2.32    0.00    0.00    1.05    0.00    0.00    0.00   95.58
  1. 軟中斷

軟中斷每秒輸出1次, 輸出6次:

# mpstat -I SCPU 1 6
09:51:22     CPU       HI/s    TIMER/s   NET_TX/s   NET_RX/s    BLOCK/s BLOCK_IOPOLL/s  TASKLET/s    SCHED/s  HRTIMER/s      RCU/s
09:51:23       0       0.00      25.00       1.00       3.00       0.00           0.00       0.00       0.00     200.00       0.00
09:51:24       0       0.00      31.00       0.00       4.00       0.00           0.00       0.00       0.00     200.00       0.00
09:51:25       0       0.00      26.00       0.00       4.00       0.00           0.00       0.00       0.00     199.00       0.00
09:51:26       0       0.00      36.00       0.00       4.00       0.00           0.00       0.00       0.00     200.00       0.00
09:51:27       0       0.00      31.00       0.00       3.00       0.00           0.00       0.00       0.00     199.00       0.00
09:51:28       0       0.00      31.00       1.00       3.00       0.00           0.00       0.00       0.00     200.00       0.00
Average:       0       0.00      30.00       0.33       3.50       0.00           0.00       0.00       0.00     199.67       0.00

對應的軟中斷信息:

# cat /proc/softirqs 
                    CPU0       
          HI:          0
       TIMER:    7778445
      NET_TX:       7244
      NET_RX:     293406
       BLOCK:          0
BLOCK_IOPOLL:          0
     TASKLET:          1
       SCHED:          0
     HRTIMER:  522165833
         RCU:          0
  1. 硬中斷

處理器硬中斷總數:

# mpstat -I SUM 1 6
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

09:54:50     CPU    intr/s
09:54:51     all   3000.00
09:54:52     all   3151.00
09:54:53     all   3229.00
09:54:54     all   3129.00
09:54:55     all   3049.00
09:54:56     all   3210.00
Average:     all   3127.79

每個硬中斷產生速率:

# mpstat -I CPU 1 6
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

09:56:46     CPU       20/s       28/s       30/s       33/s       34/s       35/s       68/s       80/s       84/s       86/s       87/s       90/s       94/s      116/s      125/s      127/s      173/s      230/s      233/s      255/s      261/s      Err/s
09:56:47       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3151.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00       5.00       0.00       0.00       0.00       0.00       0.00
09:56:48       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3140.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00       7.00       0.00       0.00       0.00       0.00       0.00
09:56:49       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3355.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00       6.00       0.00       0.00       0.00       0.00       0.00
09:56:50       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3031.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00
09:56:51       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3288.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00       6.00       0.00       0.00       0.00       0.00       0.00
09:56:52       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3155.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00       6.00       0.00       0.00       0.00       0.00       0.00
Average:       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    3186.67       0.00       2.00       0.00       0.00       0.00       0.00       0.00       5.67       0.00       0.00       0.00       0.00       0.00

對應的中斷信息:

# cat /proc/interrupts 
           CPU0       
 84: 1583852200      INTC  68  gp_timer
 86:        108      INTC  70  44e0b000.i2c
 87:     483682      INTC  71  4802a000.i2c
 90:         59      INTC  74  UART2
173:     701497      GPIO  29  eth0
230:          6      GPIO  22  button_int
233:          0      GPIO  25  fpga_int
255:          0      GPIO  15  mmc0
261:          0      GPIO  21  power_int
Err:          0
......

啓動appA應用前,可以看出irq爲84的中斷gp_timer中斷比較高, 3155次/S左右。

啓動應用後

先啓動應用,然後再看一下這些指標的變化。

# mpstat 1 5
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

10:10:45     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
10:10:46     all    7.95    0.00   18.18    0.00    0.00    3.41    0.00    0.00    0.00   70.45
10:10:47     all    6.17    0.00   16.05    0.00    0.00    1.23    0.00    0.00    0.00   76.54
10:10:48     all    4.94    0.00   17.28    0.00    0.00    2.47    0.00    0.00    0.00   75.31
10:10:49     all    8.05    0.00   17.24    0.00    0.00    4.60    0.00    0.00    0.00   70.11
10:10:50     all   11.76    0.00   12.94    0.00    0.00    2.35    0.00    0.00    0.00   72.94
Average:     all    7.82    0.00   16.35    0.00    0.00    2.84    0.00    0.00    0.00   72.99

usr,sys 升高明顯,soft有所升高,什麼導致的sys升高?下面看硬中斷處理信息:

# mpstat -I SUM 1 6
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

10:12:11     CPU    intr/s
10:12:12     all   6571.00
10:12:13     all   6597.00
10:12:14     all   6574.00
10:12:15     all   6566.00
10:12:16     all   6592.00
10:12:17     all   6583.00
Average:     all   6580.50

對比啓動應用前3000/s,每秒中斷產生次數增長了120%。繼續查看哪些中斷變高了?

# mpstat -I CPU 1 6
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

10:15:04     CPU       20/s       28/s       30/s       33/s       34/s       35/s       68/s       80/s       84/s       86/s       87/s       90/s       94/s      116/s      125/s      127/s      173/s      230/s      233/s      255/s      261/s      Err/s
10:15:05       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6552.00       0.00       3.00       0.00       0.00       0.00       0.00       0.00      28.00       0.00       0.00       0.00       0.00       0.00
10:15:06       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6552.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00      30.00       0.00       0.00       0.00       0.00       0.00
10:15:07       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6637.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00      27.00       0.00       0.00       0.00       0.00       0.00
10:15:08       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6543.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00      28.00       0.00       0.00       0.00       0.00       0.00
10:15:10       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6579.00       0.00       4.00       0.00       0.00       0.00       0.00       0.00      30.00       0.00       0.00       0.00       0.00       0.00
10:15:11       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6561.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00      29.00       0.00       0.00       0.00       0.00       0.00
Average:       0       0.00       0.00       0.00       0.00       0.00       0.00       0.00       0.00    6570.67       0.00       1.83       0.00       0.00       0.00       0.00       0.00      28.67       0.00       0.00       0.00       0.00       0.00
# mpstat -I SCPU 1 6
Linux 3.12.10      08/22/22        _armv7l_        (1 CPU)

10:20:29     CPU       HI/s    TIMER/s   NET_TX/s   NET_RX/s    BLOCK/s BLOCK_IOPOLL/s  TASKLET/s    SCHED/s  HRTIMER/s      RCU/s
10:20:30       0       0.00      41.58       0.00       9.90       0.00           0.00       0.00       0.00    2172.28       0.00
10:20:31       0       0.00      43.00       0.00      12.00       0.00           0.00       0.00       0.00    2187.00       0.00
10:20:32       0       0.00      38.00       0.00      11.00       0.00           0.00       0.00       0.00    2191.00       0.00
10:20:33       0       0.00      34.00       1.00      10.00       0.00           0.00       0.00       0.00    2190.00       0.00
10:20:34       0       0.00      37.00       0.00      11.00       0.00           0.00       0.00       0.00    2190.00       0.00
10:20:35       0       0.00      40.00       0.00      10.00       0.00           0.00       0.00       0.00    2184.00       0.00
Average:       0       0.00      38.94       0.17      10.65       0.00           0.00       0.00       0.00    2185.69       0.00

可以看irq爲84的中斷gp_timer中斷相比應用啓動前變爲2倍, 3155.00次/S左右。相應的高精度定時器軟中斷HRTIMER從之前的200/S上升到2100/S,到此基本可以確定應用瘋狂定時操作引起的,進一步看內核執行的函數來確認。

採集內核函數的方法

目前基本清楚因爲定時器中斷變高導致的內核函數執行時間變長,接下來採集 CPU 在 sys 飆高的瞬間所執行的內核函數,同時看看應用執行什麼引起的定時器中斷,再做相應的優化。採集內核函數的方法有很多,比如:

  • 通過 perf( perf 是內核自帶的性能剖析工具) 可以採集 CPU 的熱點,看看 sys 利用率高時,哪些內核耗時的 CPU 利用率高;

  • 通過 perf 的 call-graph 功能可以查看具體的調用棧信息,也就是線程是從什麼路徑上執行下來的;

  • 通過 perf 的 annotate 功能可以追蹤到線程是在內核函數的哪些語句上比較耗時;

  • 通過 ftrace 的 function-graph 功能可以查看這些內核函數的具體耗時,以及在哪個路徑上耗時最大。

  • 針對一些瞬時sys 利用率高問題,還可以通過系統快照sysrq,把當前 CPU 正在做的工作記錄下來,然後結合內核源碼分析爲什麼 sys 利用率會高。

    sysrq常用來分析內核問題的工具,用它可以觀察當前的內存快照、任務快照,可以構造 vmcore 把系統的所有信息都保存下來,甚至還可以在內存緊張的時候用它殺掉內存開銷最大的那個進程。

    用 sysrq 來分析問題,首先需要使能 sysyrq,使能sysrq後沒有任何開銷,使能方式如下:

    $ sysctl -w kernel.sysrq=1
    

    sysrq 的功能被使能後,使用它的 -t 選項來把當前的任務快照保存下來,看看系統中都有哪些任務,以及這些任務都在幹什麼。使用方式如下:

    $ echo t > /proc/sysrq-trigger
    

    執行後,任務快照會被打印到內核緩衝區,這些任務快照信息通過 dmesg 命令來查看:

    $ dmesg
    

內核採集分析

這裏使用perfappA進程進行採樣,輸出內核整個調用鏈上的彙總信息。

# 採樣10s後退出
$ perf record -a -g -p `pidof appA` -- sleep 10

採樣完成後,繼續執行 perf report命令,得到 perf 的彙總報告。

image-20220822110337136

額。。。屏幕太小、ARM 串口來打印,這打印很難看。

火焰圖分析

針對 perf 彙總數據的展示問題,Brendan Gragg 發明了火焰圖,通過矢量圖的形式,更直觀展示彙總結果。

img

(圖片來自 Brendan Gregg 博客

  • 橫軸表示採樣數和採樣比例。一個函數佔用的橫軸越寬,就代表它的執行時間越長。同一層的多個函數,則是按照字母來排序。

  • 縱軸表示調用棧,由下往上根據調用關係逐個展開。換句話說,上下相鄰的兩個函數中,下面的函數,是上面函數的父函數。這樣,調用棧越深,縱軸就越高。

另外,要注意圖中的顏色,並沒有特殊含義,只是用來區分不同的函數。

火焰圖是動態的矢量圖格式,所以它還支持一些動態特性。比如,鼠標懸停到某個函數上時,就會自動顯示這個函數的採樣數和採樣比例。而當你用鼠標點擊函數時,火焰圖就會把該層及其上的各層放大,方便你觀察這些處於火焰圖頂部的調用棧的細節。

如果我們根據性能分析的目標來劃分,火焰圖可以分爲下面這幾種。

  • on-CPU 火焰圖:表示 CPU 的繁忙情況,用在 CPU 使用率比較高的場景中。
  • off-CPU 火焰圖:表示 CPU 等待 I/O、鎖等各種資源的阻塞情況。
  • 內存火焰圖:表示內存的分配和釋放情況。
  • 熱 / 冷火焰圖:表示將 on-CPU 和 off-CPU 結合在一起綜合展示。
  • 差分火焰圖:表示兩個火焰圖的差分情況,紅色表示增長,藍色表示衰減。差分火焰圖常用來比較不同場景和不同時期的火焰圖,以便分析系統變化前後對性能的影響情況。

如何生成火焰圖?首先,下載幾個能從 perf record 記錄生成火焰圖的工具,這些工具都放在 https://github.com/brendangregg/FlameGraph

$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph

安裝好工具後,要生成火焰圖,其實主要需要三個步驟:

  • 執行 perf script ,將 perf record 的記錄轉換成可讀的採樣記錄;
  • 執行 stackcollapse-perf.pl 腳本,合併調用棧信息;
  • 執行 flamegraph.pl 腳本,生成火焰圖。

如果你的機器安裝了完整的系統,通過管道簡化這三個步驟的執行如下:

$ perf script -i /root/perf.data | ./stackcollapse-perf.pl --all |  ./flamegraph.pl > hsys.svg

因爲我的目標系統是在ARM busybos上,先在目標機器上執行步驟1,在將中間文件上傳到X86繫上完成步驟2和3。

$ perf script -i perf.data > perf-temp
$ tftp -p 192.168.1.55 -l perf-temp 

X86機器上完成步驟2和3.

$ cat perf-temp| ./stackcollapse-perf.pl --all |  ./flamegraph.pl > hsys.svg

使用瀏覽器打開hsys.svg如下。

image-20220822112314959

從下往上看,沿着調用棧中最寬的函數來分析執行次數最多的函數。看到的結果跟的 `perf report 類似,但直觀了很多,這幾團系統調用的火焰,就是我們關注的地方。

可以看到,應用發起的幾個系統調用後,內核sys_rt_sigtimedwaitsys_mq_timedsendsys_mq_timedreceive分別佔總執行時間的22.69%、(13.71%+6.15%)、(8.53%+8.89%)。

image-20220822112454415

到這裏,找出了內核執行最頻繁的函數調用堆棧,而這個堆棧中的各層級函數,就是潛在的性能瓶頸來源。對比同一cpu高版本內核,該問題並不明顯,如果想深入瞭解linux時間子系統,請移步時間子系統-蝸窩科技。看來內核是動不了,不好下手,那回到應用看看。

三、解決

既然已經清楚原因是應用定時相關操作引起的,回到應用排查相關代碼。

這個應用是一個協議棧,整個協議棧有多個不同優先級的實時線程,每個線程負責各部分通訊處理,其中兩個線程爲整個協議棧提供timers處理機制,線程A向操作系統註冊一個週期timer,該週期timer的間隔就是協議棧軟件timer的基準時間,線程A週期等待操作系統timer超時信號。

當超時信號到達時,線程A遍歷所有上層應用註冊的timer,判斷時間是否到期,如果該timer到期,通過消息隊列mq將該timer發送給另一個線程B進行timer處理,線程B接收到該timer後,執行該timer註冊的超時回調函數callbalk_timeout

排查後發現,問題出在線程A向操作系統註冊的這個週期timer,它的觸發間隔爲500us,由於觸發頻率太高,導致操作系統處理定時器軟硬中斷消耗大部分CPU,表現就是系統cpu飆高,但是檢索整個協議棧,並沒有發現上層應用註冊小於1ms的timer,協議棧根本用不到這麼小基準的timer。

所以,解決的辦法很簡單,就修改一行代碼,將線程A向操作系統註冊的週期timer間隔500us調整爲1ms,問題解決。

優化後,時鐘軟硬中斷降低。系統sys CPU利用率下降(65%->13%),如下:

$ pidstat  1 5 -p `pidof appA` #目標應用指標
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

17:32:45      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
17:32:46        0      1112    8.91    0.99    0.00    0.00    9.90     0  appA
17:32:47        0      1112    3.00    8.00    0.00    0.00   11.00     0  appA
17:32:48        0      1112    8.00    3.00    0.00    0.00   11.00     0  appA
17:32:49        0      1112   10.00    1.00    0.00    0.00   11.00     0  appA

$mpstat  -A   1 1 #系統整體詳細指標
Linux 3.12.10       08/22/22        _armv7l_        (1 CPU)

17:35:24     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
17:35:25     all    1.20    0.00   10.84    0.00    0.00    0.00    0.00    0.00   87.95
17:35:25       0    1.20    0.00   10.84    0.00    0.00    0.00    0.00    0.00   87.95

17:35:24     CPU    intr/s
17:35:25     all   6821.69
17:35:25       0   6820.48

17:35:24     CPU      20/s      28/s      30/s      33/s      34/s      35/s      68/s      80/s      84/s      86/s      87/s      90/s      94/s     116/s     125/s     127/s     173/s     230/s     233/s     255/s     261/s     Err/s
17:35:25       0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00   6792.77      0.00      3.61      0.00      0.00      0.00      0.00      0.00     24.10      0.00      0.00      0.00      0.00      0.00

17:35:24     CPU      HI/s   TIMER/s  NET_TX/s  NET_RX/s   BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s  RCU/s
17:35:25       0      0.00     30.12      1.20     13.25      0.00      0.00      0.00      0.00   1443.37      0.00

Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
Average:     all    1.20    0.00   10.84    0.00    0.00    0.00    0.00    0.00   87.95
Average:       0    1.20    0.00   10.84    0.00    0.00    0.00    0.00    0.00   87.95

真是debug一兩天,最後改一行代碼解決問題o(╯□╰)o)。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章