Try   HackMD

Profiling

取自:Brendan Gregg

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

先概覽一下,右側有顏色的方塊代表在 Linux 的硬體或軟體 Component ,黑字分別是可以用的 tool 去 trace 相關的效能議題。

  • 有哪些 tool ?
    • perf
  • page faults
    • major
    • minor

視覺化理解

✔ 命中

✔ 命中

❌ Cache Miss

→ 更新 Cache

❌ TLB Miss

✔ 有對應頁面

❌ 沒有記錄

→ 載入頁面至 RAM

→ 更新頁表 + TLB

🧠 CPU

📘 TLB (快取頁面對應)

⚡ Cache (L1/L2/L3)

✅ 成功讀資料

🧠 RAM

📄 Page Table (在 RAM)

📥 更新 TLB

🚨 Page Fault

💽 Disk / Swap

虛擬記憶體存取流程圖

CPU 存取虛擬地址 VA
       │
       ▼
查 TLB 有無映射(VA → PA)
       │
       ├─✔ 有映射 → 取出實體位址 PA
       │         │
       │         ▼
       │     查 CPU Cache(L1 → L2 → L3)
       │         │
       │         ├─✔ 有資料 → Cache Hit ✅(最快)
       │         │
       │         └─✘ 無資料 → Cache Miss 🚫
       │                     │
       │                     ▼
       │              從 RAM 載入資料 → 更新 Cache → 使用
       │
       └─✘ 無映射 → TLB Miss
                  │
                  ▼
          查頁表有無該頁面資訊
                  │
          ├─✔ 有:更新 TLB → 回到查 Cache
          │
          └─✘ 無:**Page Fault** ⚠
                         │
                  通知 OS → 從 Disk/SWAP 載入頁面至 RAM
                         ▼
                     更新頁表、TLB → 重啟指令 → 查 Cache

Page Fault

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • 程式在執行時,嘗試存取一塊目前不在實體記憶體中的虛擬記憶體頁面,導致處理器產生例外中斷,由作業系統處理。
  • backgound
    • Linux 使用 虛擬記憶體(Virtual Memory):每個進程看到的是獨立的虛擬地址空間
    • 作業系統會將虛擬記憶體分割為 一頁一頁(Page),通常是 4KB/頁
    • 只有程式實際存取頁面時,才會把它從磁碟載入實體記憶體(lazy loading)
類型 描述 是否正常
Minor page fault 頁面不在 process 的 page table 中,但已在實體記憶體中(如 shared memory) ✅ 正常,快速處理
Major page fault 該頁尚未載入,必須從磁碟或 swap 中讀取 ⚠️ 成本高,會造成延遲
Invalid (segfault) 試圖存取不存在的頁面,或權限錯誤(如寫入唯讀區) ❌ 系統會發出 SIGSEGV,程式當掉

page fault

┌────────────────────────────┐
│  User Program              │
│  ───────────────────────  │
│  int *p = malloc(4096);    │
│  p[0] = 123;   ← 觸發頁錯誤│
└────────────┬───────────────┘
             │
             ▼
┌────────────────────────────┐
│  CPU 檢查 Page Table        │
│  → 發現該頁不存在(invalid)│
└────────────┬───────────────┘
             │
             ▼
┌────────────────────────────┐
│  發出 Page Fault 中斷       │
│  → 進入 kernel 模式         │
└────────────┬───────────────┘
             │
             ▼
┌────────────────────────────┐
│  Linux Kernel Page Fault Handler │
│  檢查:                          │
│  ─ 這個地址合法嗎?              │
│  ─ 有權限存取嗎?                │
│  ─ 是否是映射檔案?              │
└────────────┬───────────────┘
             │
     合法    │     非法(非法記憶體)
             │              │
             ▼              ▼
┌────────────────────────────┐     ┌────────────────────────────┐
│  從磁碟 / swap 載入該頁面     │     │  傳送 SIGSEGV,程式崩潰     │
│  或從 shared memory 拷貝     │     │  Segmentation Fault 錯誤訊息│
└────────────┬───────────────┘     └────────────────────────────┘
             │
             ▼
┌────────────────────────────┐
│  更新 Page Table(將虛擬頁→實體頁) │
└────────────┬───────────────┘
             │
             ▼
┌────────────────────────────┐
│  程式重新執行剛才那一行       │
│  成功寫入 p[0] = 123          │
└────────────────────────────┘

  • major vs minor
┌────────────┐
│ Page Fault │
└────┬───────┘
     ▼
┌────────────┐
│ 該頁是否已載入? │
└──┬───────────┘
   │Yes(在記憶體)
   ▼
┌──────────────┐
│ Minor Page Fault │
│ → 建立對應 page table│
└──────────────┘

   │No(需從磁碟讀取)
   ▼
┌──────────────┐
│ Major Page Fault │
│ → 讀磁碟 → 分配頁面│
└──────────────┘

page fault vs cache miss

項目 Page Fault(分頁錯誤) Cache Miss(快取未命中)
發生時機 存取的虛擬頁面尚未載入至主記憶體(RAM) 存取的資料不在 L1/L2/L3 cache 中
處理方式 由 OS 介入處理,可能從磁碟載入資料至 RAM 由硬體自動從主記憶體(RAM)載入資料到快取中
成本(延遲) 非常高,若需從磁碟(或 swap)載入頁面 中等偏低,取決於從哪層 cache 到 RAM 的距離
是否中斷程式 是,會觸發 exception,由作業系統處理 否,通常程式繼續執行,只是稍慢
發生原因 虛擬頁尚未對應至有效的實體記憶體 欲讀取的資料未被 cache 命中
與記憶體的關係 涉及虛擬記憶體管理與頁面交換 涉及 CPU cache 系統與資料區域性
  • 直覺理解:
    • Cache Miss:像你打開冰箱找牛奶,沒看到 → 去儲藏室(RAM)找到了 → 下次記得先放冰箱。
    • Page Fault:你打開冰箱找牛奶,發現牛奶根本沒買 → 出門去超商買回來 → 下次記得先放家裡冰箱。

發生路徑上的關聯:
CPU 想要讀某個位址 → 查 TLB 是否有對應實體頁(若無則 TLB miss)

若頁面尚未載入 RAM → Page Fault

若頁面已在 RAM,接下來查 cache → 若不在 cache → Cache Miss

如果在 cache → Cache Hit(最快)

總結一句話:
Cache Miss 是 CPU 找資料時,快取裡沒命中,需要去 RAM。

Page Fault 是 CPU 找資料時,那頁根本還不在 RAM,需要作業系統把它從磁碟讀進 RAM。

TLB miss

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

TLB

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Introduction to Memory Management in Linux

CPU 

命中

未命中

合法 PTE

不存在 / 權限錯誤

換頁 / 配頁

Core

ALU / AGU
產生虛擬位址

Virtual Addr

TLB
快取查詢

👉 實體記憶體存取

MMU
分頁表走訪

回填 TLB

⚡ Page Fault

OS Page-Fault Handler
(核心模式)

更新分頁表
+ 刷新 TLB

返回使用者態,重新執行指令

項目名稱 中文名稱 說明 常見用途
cycles CPU 週期數 處理器執行的總時鐘週期數 評估程式執行時間(與 CPU 頻率相關)
instructions 指令數 已執行的 CPU 指令總數 搭配 cycles 計算 IPC(每週期指令數)
minor-faults 次級缺頁 記憶體頁已存在但尚未映射進行處理(無需 I/O) 分析虛擬記憶體效能
major-faults 重大缺頁 缺頁時需從磁碟讀取資料(需進行 I/O) 檢查是否頻繁觸發記憶體分頁
dTLB-loads 資料 TLB 載入 嘗試載入資料的 TLB(Translation Lookaside Buffer)記錄次數 檢查記憶體虛實轉換快取的使用狀況
dTLB-load-misses 資料 TLB 失敗 資料存取時未命中 TLB,需查詢頁表 觀察 TLB 效率,失敗率高表示轉換緩慢
iTLB-loads 指令 TLB 載入 嘗試載入指令的 TLB 次數 評估程式碼虛擬位址快取命中率
iTLB-load-misses 指令 TLB 失敗 指令存取時未命中 TLB,需查詢頁表 指令分頁效率分析,常見於大型 binary
page-faults 頁面錯誤總數 包含 minor 與 major fault 的總數 觀察程式在記憶體管理上的負擔
cache-references 快取存取次數 CPU 對 L1/L2/LLC 等快取的總存取次數 衡量快取使用頻繁程度
cache-misses 快取未命中次數 CPU 存取快取時未命中的次數 評估資料/指令是否常被 cache 命中
  • perf
    • sudo perf stat
    • sudo perf record -g
  • TODO
    • gprof
階段 目的 常用指令 / 工具
快速量化 (粗粒度) 先確認 CPU/記憶體/IO 是否飽和,判斷是哪一類瓶頸 tophtopvmstatiostatdstat
抽樣分析 (中粒度) 找出「最耗時的函式/路徑」 perf stat / top / record + reportBPFtraceFlameGraph
精細診斷 (細粒度) 深入微架構(Cache、分支、TLB 等)、鎖競爭、系統呼叫 perf stat -e ...perf annotateperf c2c / lockftraceeBPF + BCC/BPFtracevalgrind (massif/cachegrind)

perf、ftrace、eBPF、kgdb、kprobes

效能分析的本質是:

🔑 效能(Performance)= 有用的工作(Useful Work)÷ 所花的時間(Time)

換句話說:

  • 我們的程式到底做了多少有意義的工作?
  • 做這些工作的過程中花掉的時間在哪裡?
  • 如果慢,到底慢在什麼資源?如何確認?

什麼是效能?

效能可以被分解為最根本的三個資源維度:

資源類型 代表指標 第一性原理的根本限制
CPU 指令數量、IPC、使用率 CPU 是有限的時脈驅動狀態機,只能每 cycle 做有限工作
Memory cache miss、TLB、page fault 記憶體存取受到階層結構與時延限制(memory wall)
I/O IO wait、latency 裝置響應時間、佇列設計、吞吐量有限
資源類型 第一性限制(根本物理限制) 常見瓶頸現象
🖥️ CPU 每個 cycle 執行固定數量的指令 CPU 飽和、IPC 太低
💾 記憶體 存取速度受到階層結構與 latency 限制 Cache miss、Memory wall、TLB miss
🔄 I/O 外部裝置存取速度受限於硬體 I/O wait、device latency

為何要進行效能分析?

在資源限制的前提下,盡可能提升有用工作的比例,減少無效等待或浪費。

效能劣化的基本來源有哪些?(來自物理與系統設計限制)

基本現象 第一性原因解釋
CPU 飽和 指令數量過多、無法並行處理、等待其他資源(如記憶體)
Cache Miss 資料位置分散、不符合 spatial/temporal locality
Page Fault 虛擬記憶體機制導致非預期 I/O(memory access latency)
Context Switch 多任務切換導致保存/還原狀態、TLB/cache flush 等 overhead
Lock Contention 多執行緒同時搶奪資源,造成序列化
I/O Bottleneck 磁碟、網路等裝置 throughput 無法滿足需求

效能分析的工具選擇與使用時機

階段 你在問的問題 常見工具 工具特性
① 基本時間量測 程式整體到底花多少時間? time, hyperfine 快速簡單,取得初步效能數據
② 系統資源使用 CPU/Memory/I/O 哪個資源是瓶頸? top, htop, vmstat, iotop 快速定位整體瓶頸資源
③ CPU 細部分析 IPC 高嗎?Cycles 浪費在哪? perf stat, ocperf, pmu-tools 提供細緻的 CPU 硬體指標
④ 熱點函式定位 哪個函式或程式段落最耗時? perf record/report, gprof, FlameGraph 提供函式級別精確定位
⑤ 函式呼叫關係 函式呼叫誰、多少次、每次多久? uftrace, ltrace 清楚展示 call graph
⑥ 記憶體耗用 記憶體用多少?峰值在哪裡? valgrind massif, heaptrack 檢查記憶體峰值、leak
⑦ Cache行為分析 L1/L2 cache有效使用嗎? cachegrind, callgrind, perf c2c 展示 Cache 細部效能
⑧ 低負擔線上追蹤 Production 可用的長期低成本追蹤? eBPF (bcc, bpftrace) 長期觀測,幾乎不影響效能

整體分析流程(效能分析 Roadmap)

① 量化整體時間 time/hyperfine

② 觀察資源使用 top/iotop

③ 深入 CPU 指標 perf stat

④ 熱點函式 perf record + FlameGraph

⑤ 函式呼叫關係 uftrace

⑥ 記憶體峰值 massif

⑦ Cache 行為 cachegrind

⑧ 長期追蹤 eBPF/bpftrace

分析流程

  1. 觀察輸出行為

    • 為什麼慢?延遲在哪?
    • 常用指標:wall-clock time、throughput、latency
  2. 建立模型

    • T = CPU Time + Wait Time + I/O Time
    • CPU Time = Instructions / IPC × Cycle Time
  3. 找出資源瓶頸

    • 工具:perf、uftrace、ftrace、top、iotop、vmstat 等
  4. 回推到根本原因

    • 是不是 cache miss?是不是鎖?是不是 syscall?是不是 scheduler 不公平?是不是 memory bandwidth 被佔用?
  5. 優化策略根據本質制定

    • 減少 I/O 次數(避免 page fault)
    • 減少 thread 間 contention(使用 lock-free 結構)
    • 利用資料 locality(避免 cache miss)
    • 改寫演算法使得計算時間減少(例如 FFT vs naive DFT)

perf

用途

perf 是 Linux 下用於分析效能瓶頸的核心工具,適用於從系統層級到函式層級的 CPU profiling、cache miss 分析與系統事件統計。

使用時機

  • 想知道「哪裡跑得最久」:找出 CPU 熱點與 call graph
  • 想知道「整體是否有效率」:透過 perf stat 觀察 IPC、cache miss、cycles
  • 需要生成火焰圖以視覺化熱點區段時
  • 觀察 kernel 與 userspace 混合行為

常見用法

# 統計整體效能指標(IPC、cache miss 等)
perf stat ./your_program

# 收集 call graph 與 hot spot 分析
perf record -g ./your_program
perf report

# 火焰圖:輸出資料並產生視覺圖形
perf record -F 99 -a -g -- ./your_program
perf script > out.perf
stackcollapse-perf.pl out.perf > out.folded
flamegraph.pl out.folded > flamegraph.svg

優點

  • 不需修改程式即可進行 profiling
  • 支援多種硬體事件(如 instructions、cycles、cache miss)
  • 可深入分析 kernel 與 user 空間行為
  • 可輸出火焰圖等視覺化工具

缺點

  • 有些功能需 root 權限執行
  • 精確分析需符號表與 debugging symbols
  • 在 container 或 restricted 環境下可能受限

實務建議

  • 進行任何優化前,先用 perf stat 量化基本效能
  • perf top 即時觀察熱點,再進行 perf record 深入分析
  • 建議搭配火焰圖視覺化觀察瓶頸集中區域
  • 生產環境建議短時取樣,避免過多 overhead

ftrace

用途

ftrace 是 Linux kernel 內建的 tracing framework,用於追蹤核心行為(context switch、IRQ latency、function call、syscall 等),可幫助系統工程師深入分析排程延遲、系統抖動(jitter)與低階效能問題。

使用時機

  • 排查系統異常抖動、長時間卡住的 root cause
  • 驅動程式與 kernel module 的效能瓶頸分析
  • 觀察 scheduler 運作、task 切換行為
  • perf 搭配進行全系統觀察

常見用法

# 啟用 function tracer
sudo echo function > /sys/kernel/debug/tracing/current_tracer

# 過濾特定函式,例如 do_sys_open
sudo echo do_sys_open > /sys/kernel/debug/tracing/set_ftrace_filter

# 啟動與觀察 trace
sudo cat /sys/kernel/debug/tracing/trace

# 清除 trace buffer
sudo echo > /sys/kernel/debug/tracing/trace

也可透過 wrapper 工具如 trace-cmdkernelshark 提供更佳的使用體驗與 GUI 支援。

優點

  • 原生支援於 kernel,無需額外安裝
  • 能夠精確觀察 scheduler 與 interrupt 層級活動
  • 適合分析 low-latency 系統與 driver
  • 支援多種 tracer(function、irqsoff、sched_switch 等)

缺點

  • 操作複雜,需要熟悉 debugfs 與 kernel trace 環境
  • 輸出資訊可能過於繁雜,需搭配過濾條件使用
  • 僅適用於 kernel space,不適合使用者空間 profiling

實務建議

  • 若觀察 jitter 或 latency spike,先從 sched_switch 分析上下文切換時間
  • 可搭配 trace-cmd report 彙整更清晰的事件序列
  • 驅動或 kernel module 最佳化常需搭配 ftrace 佐證

火焰圖分析(Flamegraph)

用途

火焰圖是一種視覺化工具,用於顯示 CPU 使用的函式堆疊與時間分佈。它可以幫助你一眼看出哪些函式佔用最多執行時間,適合用於分析 hot path 與瓶頸函式。

使用時機

  • 程式效能下降但原因不明
  • 想快速定位執行時間集中在哪些函式
  • 與團隊溝通效能瓶頸時需要可視化證據
  • 對函式調用深度與寬度有整體認知需求

建立流程(以 perf 為例)

安裝工具(以 Ubuntu 為例)
sudo apt install linux-tools-common linux-tools-$(uname -r) \
    git build-essential -y

# 安裝 flamegraph 腳本集
git clone https://github.com/brendangregg/Flamegraph.git
cd Flamegraph
收集資料並產出圖表
# 收集 profile 資料(建議搭配 -F 控制取樣頻率)
perf record -F 99 -a -g -- ./your_program

# 轉換成 stack trace 格式
perf script > out.perf

# 產出 flamegraph 所需格式
./stackcollapse-perf.pl out.perf > out.folded

# 繪製火焰圖
./flamegraph.pl out.folded > flamegraph.svg

火焰圖說明

  • 橫向長度:表示總執行時間佔比(越長越慢)
  • 縱向堆疊:表示函式呼叫鏈深度
  • 每一條方塊:表示一個函式,可滑鼠懸停看名稱與時間

優點

  • 非常直觀、便於發現效能熱點(hot path)
  • 不需修改原始碼,可在已編譯程式上取樣
  • 適合大規模應用程式的全域視角效能分析

缺點

  • 須搭配 perf 或其他 trace 工具收集資料
  • 多執行緒與 async 程式容易堆疊混亂,需清理堆疊資料
  • 需花時間學習解讀技巧

實務建議

  • 可定期在開發週期進行一次火焰圖分析做為基準比較
  • 遇到 CPU 使用異常高時優先繪製火焰圖定位
  • 可搭配分支、cache miss 事件產生不同維度火焰圖

valgrind

用途

valgrind 是一套動態分析工具,最常用於記憶體錯誤偵測、資源洩漏分析與 cache 效能模擬。

核心工具包含:

  • memcheck: 偵測未初始化記憶體讀取、非法存取、記憶體洩漏等問題
  • cachegrind: 分析 cache miss、分支預測命中率
  • callgrind: 分析函式呼叫次數與執行時間(可搭配 KCachegrind 視覺化)

使用時機

  • 開發早期測試記憶體正確性與釋放邏輯
  • 找出 segmentation fault 原因
  • 想量化不同演算法對 cache 行為的影響(例如排序)
  • 搭配 callgrind 作為簡易函式 profiling 工具

常見用法

# 偵測記憶體使用錯誤與洩漏
valgrind --leak-check=full ./your_program

# 偵測未初始化記憶體、越界存取
valgrind --track-origins=yes ./your_program

# 分析 cache 行為
valgrind --tool=cachegrind ./your_program
cg_annotate cachegrind.out.*

# 函式呼叫分析
valgrind --tool=callgrind ./your_program
callgrind_annotate callgrind.out.*

優點

  • 偵錯能力強,可定位難以重現的記憶體問題
  • 可模擬硬體 cache 行為,便於調整資料結構或演算法
  • callgrind 與 cachegrind 支援詳細報告與視覺化工具(如 KCachegrind)

缺點

  • 執行非常慢,通常為原始速度的 10~50 倍
  • 不支援多執行緒間同步錯誤偵測
  • 僅支援 x86/x86_64 架構,其他平台相容性差

實務建議

  • 開發初期強烈建議整合至測試流程中,提早發現潛藏 bug
  • 對效能敏感邏輯(如排序、數值計算)可搭配 cachegrind 模擬最佳化潛力
  • 呼叫行為分析可導出 callgrind 檔並使用 KCachegrind 互動視覺化分析

gdb + core dump 分析流程

為何需要?

在生產環境中,程式可能會因為 segmentation fault、abort、assertion fail 等異常崩潰。當你無法重現這些錯誤時,core dump 是唯一可以還原當下狀態的線索來源

使用 gdb 搭配 core dump 可以:

  • 查看崩潰當下是哪一行程式碼造成的錯誤
  • 還原呼叫堆疊(stack trace)與變數內容
  • 檢查記憶體狀態與指標有效性
  • 協助判斷邏輯錯誤、資源釋放順序等問題

使用時機

  • 程式 crash 且無法用 valgrind 或 log 重現時
  • 使用者回報錯誤但無法完整還原操作流程時
  • CI/CD 環境中需自動收集失敗樣本進行事後分析

如何啟用 core dump?

# 設定 core dump 開啟與檔案大小(ulimit 預設為 0)
ulimit -c unlimited

# 指定 core 檔儲存路徑(Linux)
echo "/tmp/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

撰寫可除錯的程式(加上 debug symbol)

g++ -g -o myprog main.cpp

使用 gdb 分析 core dump

# 假設 myprog 是執行檔,core.12345 是 dump 檔
gdb ./myprog core.12345

# 在 gdb 內常用指令:
(gdb) bt                 # 顯示 backtrace(函式呼叫堆疊)
(gdb) frame 0            # 切到崩潰當下的堆疊框架
(gdb) info locals        # 查看區域變數
(gdb) list               # 顯示錯誤行附近原始碼

優點

  • 還原問題現場,不必重現也能追蹤 root cause
  • 可搭配 CI/CD 自動收集並分類 crash case
  • 支援多種架構與平台(cross-debug 也可)

缺點

  • 需事先保留 core 檔與 debug symbol
  • 無法分析邏輯錯誤(只能抓 runtime 異常)
  • 不適合即時效能 profiling(但可作為後續定位工具)

實務建議

  • 開發階段加入 -g 編譯選項與 INHIBIT_PACKAGE_STRIP = "1"(如 Yocto)
  • 保留執行檔與 core dump,並統一命名便於自動分析
  • 可撰寫腳本自動用 gdb 對 core dump 執行 bt 並儲存堆疊

TODO

  • vmstat 1
  • iostat 1
  • network
    • netstat -a
  • sar
  • mpstat 1
  • pidstat