性能分析小案例系列,可以通過下面鏈接查看哦
https://www.cnblogs.com/poloyy/category/1814570.html
前提
前面有學到 Buffer 和 Cache 的概念:https://www.cnblogs.com/poloyy/p/13503848.html
我們來簡單復習下
Buffer 和 Cache 的設計目的
為了提升系統的 I/O 性能,它們利用內存,充當起慢速磁盤和快速 CPU 之間的橋梁,可以加速 I/O 的訪問速度
Buffer 和 Cache
- BUffer:對磁盤的讀寫數據緩存
- Cache:對文件系統的讀寫數據緩存
讀寫角度
- 從寫的角度來說,不僅可以優化磁盤和文件的寫入,對應用程序也有好處,應用程序可以在數據真正落盤前,就返回去做其他工作
- 從讀的角度來說,不僅可以提高那些頻繁訪問數據的讀取速度,也可以降低頻繁 I/O 對磁盤的壓力
引入主題
既然 Buffer 和 Cache 對系統性能有很大影響,那我們在軟件開發的過程中,能不能利用這 一點,來優化 I/O 性能,提升應用程序的運行效率呢? 答案自然是肯定的
緩存命中率
靈魂拷問
我們想利用緩存來提升程序的運行效率,應該怎么評估這個效果呢?換句話說,有沒有哪個指標可以衡量緩存使用的好壞呢?
靈魂回答
- 緩存命中率
- 指直接通過緩存獲取數據的請求次數,占所有數據請求次數的百分比【使用緩存請求次數 / 總請求次數】
- 命中率越高,表示使用緩存帶來的收益越高,應用程序的性能也就越好
緩存的重要性
- 緩存是現在所有高並發系統必需的核心模塊
- 主要作用:把經常訪問的數據(熱點數據),提前讀入到內存中,下次訪問時就可以直接從內存讀取數據,而不需要經過硬盤,從而加快應用程序的響應速度
cachestat、cachetop
獨立的緩存模塊通常會提供查詢接口,方便我們隨時查看緩存的命中情況
不過 Linux 系統中並沒有直接提供這些接口,所以這里要介紹一下,cachestat 和 cachetop ,它們正是查看系統緩存命中情況的工具
- cachestat 提供了整個操作系統緩存的讀寫命中情況
- cachetop 提供了每個進程的緩存命中情況
Ubuntu 安裝工具
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD echo "deb https://repo.iovisor.org/apt/xenial xenial main" | sudo tee /etc/apt/sources.list.d/iovisor.list sudo apt-get update sudo apt-get install -y bcc-tools libbcc-examples linux-headers-$(uname -r)
配置環境變量
vim /etc/profile # 在文件結尾處添加 export PATH=$PATH:/usr/share/bcc/tools # 保存文件后 source /etc/profile
cachestat
# 它以 1 秒的時間間隔,輸出了 3 組緩存統計數據 cachestat 1 3
字段說明
cachetop
cachetop
- 默認按照緩存的命中次數(HITS)排序,展示了每個進程的緩存命中情況
- 具體到每一個指標,這里的 HITS、MISSES 和 DIRTIES ,跟 cachestat 里的含義 一樣,分別代表間隔時間內的緩存命中次數、未命中次數以及新增到緩存中的臟頁數
- READ_HIT:讀的緩存命中率
- WRITE_HIT:寫的緩存命中率
查看文件的緩存大小
可以使用 pcstat 這個工具,來查看文件在內存中的緩存大小以及緩存比例
安裝 pcstat
# 安裝 go apt install golang-go vim /etc/profile # 在文件結尾處添加 export GOPATH=~/go export PATH=~/go/bin:$PATH # 保存文件后 source /etc/profile go get golang.org/x/sys/unix go get github.com/tobert/pcstat/pcstat
運行 pcstat
pcstat /bin/ls
Cached 就是 /bin/ls 在緩存中的大小,而 Percent 則是緩存的百分比,看到它們都是 0,這說明 /bin/ls 並不在緩存中
執行 ls 命令,再來查看
ls pcstat /bin/ls
發現都在緩存中了
講案例前的准備
- 系統:Ubuntu 18.04,當然同樣適用於其他的 Linux 系 統。
- 機器配置:2 CPU,2 GB 內存
- 預先按照上面的步驟安裝 bcc 和 pcstat 軟件包,並把這些工具的安裝路徑添加到到 PATH 環境變量中
- 預先安裝 Docker 軟件包: apt-get install docker.io
- 打開兩個終端同時連到 Linux 上
案例一:通過 dd 寫入讀取文件
注意:沒說第幾個終端都是默認第一個終端執行命令哦
dd 命令生成一個臨時文件
# 生成一個 512MB 的臨時文件 dd if=/dev/sda1 of=file bs=1M count=512 # 清理緩存 echo 3 > /proc/sys/vm/drop_caches
運行 pcstat 查看 file 文件
pcstat file
確認剛剛生成的文件不在緩存中。如果一切正常, 會看到 Cached 和 Percent 都是 0
運行 cachetop
# 每隔 1 秒刷新一次數據 cachetop 1
第二個終端運行 dd 命令
dd if=file of=/dev/null bs=1M
- 從 dd 的結果可以看出,這個文件的讀性能是 639 MB/s
- 由於在 dd 命令運行前我們已經清理了緩存,所以 dd 命令讀取數據時,肯定要通過文件系統從磁盤中讀取
查看 cachetop
可以看到 dd 命令並不是所有的讀都落到了磁盤上,讀請求的緩存命中率只有 50%
第二個終端再次運行 dd 命令
dd if=file of=/dev/null bs=1M
磁盤的讀性能蹭蹭蹭往上漲,去到了 1.6GB/s
再查看 cachetop
可以發現,這次讀的緩存命中率是 100%
第二個終端運行 pcstat
pcstat file
測試文件已經被全部緩存起來了,和剛剛 cachetop 觀察到緩存命中率 100% 是一致的
總結
這兩次結果說明,系統緩存對第二次 dd 操作有明顯的加速效果,可以大大提高文件讀取的性能。
案例二
前提
- 這里運行了一個不可中斷狀態的進程
- 功能:是每秒從磁盤分區 /dev/sda1 中讀取 32MB 的數據, 並打印出讀取數據花費的時間
查看應用日志
從這里可以看到,每讀取 32 MB 的數據,就需要花 0.9 秒
靈魂拷問
這個時間合理嗎?這也太慢了吧,那這是不是沒用系統緩存導致的呢?
查看 cachetop
結果分析
讀的命中率雖然是 100%,命中次數是 1024,看起來全部的讀請求都經過了系統緩存
靈魂又拷問了
全都是緩存 I/O,讀取速度不應該這么慢
深入分析
- 另一個重要因素,每秒實際讀取的數據大小,HITS 代表緩存的命中次數,那么每次命中能讀取多少數據呢?自然是一頁
- 內存以頁為單位進行管理,而每個頁的大小是 4KB
- 所以,在 5 秒的時間間隔 里,命中的緩存為 1024*4K/1024 = 4MB,再除以 5 秒,可以得到每秒讀的緩存是 0.8MB,顯然跟案例應用的 32 MB/s 相差太多
結果猜想
這個案例估計沒有充分利用系統緩存,如果系統調用設置直接 I/O 的標志,就可以繞過系統緩存
通過 strace 觀察系統調用
strace -p $(pgrep app)
結果分析
- 從 strace 的結果可以看到,案例應用調用了 openat 來打開磁盤分區 /dev/sdb1,並且傳 入的參數為 O_RDONLY|O_DIRECT(中間的豎線表示或)
- O_RDONLY 表示以只讀方式打開
- 而 O_DIRECT 則表示以直接讀取的方式打開,這會繞過系統的緩存
- 驗證了這一點,就很容易理解為什么讀 32 MB 的數據就都要那么久了
- 直接從磁盤讀寫的速度,自然遠慢於對緩存的讀寫,這也是緩存存在的最大意義了
查看應用源碼
它果然用了直接 I/O
修改源代碼
刪除 O_DIRECT 選項,讓應用程序使用緩存 I/O ,而不是直接 I/O,就可以加速磁盤讀取速度
驗證修復后的應用
查看新應用的日志
結果分析
現在,每次只需要 0.03 秒,就可以讀取 32MB 數據,明顯比之前的 0.9 秒快多了。所以,這次應該用了系統緩存
再次查看 cachetop
結果分析
- 果然,讀的命中率還是 100%,HITS (即命中數)卻變成了 40960
- 同樣的方法計算一 下,換算成每秒字節數正好是 32 MB(即 40960*4k/5/1024=32M)
總結
- 在進行 I/O 操作時,充分利用系統緩存可以極大地提升性能。
- 但在觀察緩存命中率時,還要注意結合應用程序實際的 I/O 大小,綜合分析緩存的使用情況
擴展
為什么優化前,通過 cachetop 只能看到很少一部分數據的全部命中,而沒有觀察到大量數據的未命中情況呢?
回答
是因為,cachetop 工具並不把直接 I/O 算進來