contributed by < foxhoundsk
>
本量測使用 eBPF 將 kernel 端的量測程式掛載 (hook) 到特定系統呼叫 (以 getuid(2)為例) 的進入點,也就是 __x64_sys_getuid
。user 端則是使用以 vDSO 實做的系統呼叫 clock_gettime(3) 來提取 timestamp。此外,kernel 端的掛載點 (hook point) 我們分別使用了 kprobe 以及 kretprobe,以量測進入/離開 kernel mode 的時間開銷。
user 端程式碼:
由實驗結果可以觀察到量測進/出 kernel 的程式有額外的執行成本,導致其兩者的量測結果比執行完整系統呼叫所花的時間還要久。
此外,使用 isolcpus 搭配 taskset 執行測試依舊得到幾乎相同的結果,推測是機器負載不重的關係,因為使用 time -v
觀察測試程式的 resource usage 得到以下輸出:
其中 Involuntary context switches
在 有/沒有 使用 isolcpus 的 CPU 時,數值幾乎相同 (個位數差距)。
值得一提的是,倘若將量測「離開 kernel」的程式碼中的 kretprobe (r:kp_sys_batch
) 改成 kprobe (p:kp_sys_batch
),量測結果會是 ~48ns。也就是說,此量測包含了 getuid
本身 (不含進出 kernel 的開銷) 的執行時間。
如果用同樣測量方式,但將「取平均值」改為「取最小值」,則:
目前想到造成額外執行成本的可能是「呼叫 clock_gettime
以及 bpf_ktime_get_ns
的間隔時間過短」,導致其中的 seqlock ([1] [2]) 發生 retry (相關資料結構在讀取過程中被 writer 修改,故需重新讀取)。或是,相關資料結構正在被修改,導致在 CS (critical section) 的進入點 (vdso_read_begin) 自旋 (spinning) (bpf_ktime_get_ns
的 CS 進入點不會有自旋的發生,因為該處用的 seqlock 是 latch sequence counter)。
若將對 seqlock 做的推測套用到上述刻意做的修改中 (將量測「離開 kernel」的程式碼中用到的 kretprobe 修改為 kprobe …),可以解釋為什麼可以得到 ~48ns 的量測結果。原因是「呼叫 clock_gettime
以及 bpf_ktime_get_ns
的間隔時間增加了」。
但若是將量測「進入 kernel」的程式碼所用到的 kprobe 修改為 kretprobe,得到的結果則是 ~128ns。如此一來這套理論就被推翻了!
不知道有沒有人對此額外開銷有什麼看法?雖然這邊沒有成功算出正確的模式切換時間開銷,但在下方對 Batch 做相關實驗時有間接算出處理器模式切換的開銷,結果為 ~23ns。
除了上述測量方式,還想到一種方法:「使用 isolcpus 以及 taskset 於 userspace 使用 RDTSC,kernelspace 的部份則以核心模組掛載 k[ret]probe 至特定系統呼叫,並於其 callback 中使用 get_cycles 提取 TSC 的內容」。
典型系統呼叫發生時,除了執行對應系統呼叫以外,還會有處理器模式切換的操作發生,後者雖然在現代處理器以及作業系統上產生的開銷相較早期已經減少許多,但如果我們可以減少這個開銷的發生次數,在系統呼叫頻繁發生的情境下 (資料庫、網頁伺服器等等),還是可以得到可觀的效能提昇。
Batch 提出一種藉由減少處理器模式切換的發生次數來增加系統呼叫效率的方法,類似手法可參見 FlexSC。
Batch 使用核心模組將 kernel 中尚未實做的系統呼叫 (__NR_afs_syscall
) 替換成自己實做的系統呼叫 (sys_batch),後者顧名思義是用於做批次 (透過單趟處理器模式切換執行多個系統呼叫) 執行系統呼叫的系統呼叫。應用程式僅需將特定資料結構 (其中包含系統呼叫號碼、參數、存放回傳值的記憶體位置等等) 做填寫,即可發出 sys_batch
系統呼叫令作業系統於單個系統呼叫中執行多個系統呼叫。
值得一提的是,Batch 可一口氣處理多個有相依性的系統呼叫,例如:open -> read -> close。同樣範例下,FlexSC 在每一個系統呼叫發出後均需等待位於 userspace 的 dispatcher 將特定資料結構做標記,以令 kernel space 中處理系統呼叫的 daemon 開始執行系統呼叫,執行完成後對應執行緒才得以往下執行下一個系統呼叫。
由此可見,FlexSC 的效能在現今 processor microarchitecture 以及作業系統中要做到效能提升的話,相對於 Batch 來說會較困難,但是 Batch 需要對應用程式做不少的改寫 (FlexSC 以 pthread library 為目標做改寫,所以應用程式僅需做些許的修改並在啟動時將 pthread library 的 shared object (.so) 做替換 (LD_PRELOAD=...
) 即可。
本實驗以 getuid(2) 系統呼叫作為實驗對象,量測典型系統呼叫以及 Batch 的效能差異。
測試程式碼:(改寫自 Batch 的範例)
單位時間內系統呼叫數量對應平均花費時間:
單位時間內系統呼叫數量對應單一系統呼叫平均花費時間:
由實驗結果可發現,在現代處理器以及作業系統 (以 Linux 為例) 上,Batch 所提出的批次處理系統呼叫的方法相較於典型系統呼叫,在密集執行 getuid
系統呼叫時可提升約 48% 的效能。
換句話說,典型系統呼叫發生時所產生的模式切換的開銷為 ~23ns。然而,實際開銷應略小於此數據,因為 Batch 在 kernel code 中有使用到 copy_to_user / copy_from_user,此倆函式隨著欲複製內容 (單次批次處理系統呼叫的數量) 大小的增長,將成為不可忽略的開銷。