在 Mac 下面調優 TiKV

在多數時候,我們都只會在 Linux 系統上面去運行 TiKV,所以很多的調優經驗都是基於 Linux 的,譬如如何使用 perf,如果使用 bcc 相關的工具,還有 ftrace 和 eBPF 這些,但在我們 team,很多同學的開發機是基於 Mac OSX,所以這裏就出現了一個不方便的場景,大家在 Mac 上面給 TiKV 開發了 feature,想看看效果好不好,可能就需要登錄到 Linux 服務器上面去運行,再用工具看看是否有性能問題。

多數時候,我們其實不需要進行那種超大規模壓力的性能測試,很多問題通過常規量小的性能測試就能夠跑出來,加上現在大家的 Mac 機器配置其實是非常的好,所以我一直覺得如果很多工作能在 Mac 上面去解決,我們就不需要在額外跑到服務器上面去折騰了。其實,Mac 上面早就給我們提供了非常強大的工具。

Instruments

如果要看性能問題,Instruments 應該是第一個選擇,而它的使用是非常的簡單,我們只需要啓動 Instruments,然後選擇要監控的應用,以及要執行的 profiling 就可以了。通常,我都會選擇 Time Profiler,以及 Allocation 這兩個 profiling,一個用來看 CPU,一個用來看 memory。這裏需要注意,對於 TiKV,我們需要使用 system 的 allocator,如果用 jemalloc,Instruments 是沒法 probe 到的。

通常,無論 CPU 還是 memory,在 CallTree 那邊我都會使用按照 thread 來,以及 Invert Call Tree,這樣更方便查看一點

下面是 CPU 和 Memory 的截圖,我們能明顯的看出來程序哪裏使用的最多,方便我們做後續的優化:

DTrace

可以看到,使用 Instruments 能非常方便的去 profile TiKV,後面這會作爲我自己優化 TiKV 的一個常規手段,畢竟真的挺方便的。不過有時候,如果我想把相關的結果發給其他同學,使用 Instruments 就不太方便了,在之前的版本里面,Instruments 還支持導出成 CSV 格式,然後通過 Flamegraph 工具生成火焰圖,但現在貌似沒這個功能了,如果有的,請麻煩聯繫我,至少我是沒找到的。

幸好,除了 Instruments,Mac 上面還有一種更強大的方案,也就是 DTrace,其實 Instruments 很多功能都是基於 DTrace 的。關於 DTrace 的語法,大家可以自行 Google,這裏不做過多討論,沒準以後我會寫一個簡單的 DTrace 教程。

CPU

下面是使用 DTrace 來 profile CPU 的腳本:

sudo dtrace -x ustackframes=100 -n 'profile-97 /pid == $target/ { @[ustack()] = count(); } tick-60s { exit(0); }' -p $1 -o out.user_stacks

上面我們採樣的週期是 97Hz,會記錄採樣的 user stack 以及對應的次數,執行 60s,然後將結果輸出到文件。

然後我們在通過 Flamegraph 生成火焰圖:

./stackcollapse.pl < out.user_stacks | ./FlameGraph/flamegraph.pl > user.svg

大概如下的樣子:

當然,我們也可以使用 Dot Graph,因爲官方的 gprof2dot 工具並不支持 DTrace,所以這裏需要使用我 folk 的版本。另外,還需要注意一個問題是 DTrace 的輸出可能是 latin 字符,但 gprof2dot 需要使用 UTF8 的,這裏我們要強制轉換一下:

iconv -f ISO-8859-1 -t UTF-8 out.user_stacks | gprof2dot.py -f dtrace

生成的圖類似:

Memory

除了 CPU,另一個我比較關心的自然就是 Memory 了,不過 Memory 就要複雜一點。在前面說過,Instruments 需要 TiKV 使用 system allocator,但有時候我不想這麼麻煩,還是想繼續用 jemalloc,這時候 Instruments 就 probe 不到了,但用 DTrace 就可以,我們只需要顯示的讓 DTrace 去 probe 相關的 jemalloc 函數就行,大概腳本如下:

pid$1::_rjem_mallocx:entry
{
    self->trace = 1;
    self->size = arg0;
}

pid$1::_rjem_mallocx:return
/self->trace == 1/
{
    @stacks[ustack(10)] = count();
    
    self->trace = 0;
    self->size = 0;
}

pid$1::_rjem_rallocx:entry
{
    self->trace = 1;
    self->size = arg0;
}

pid$1::_rjem_rallocx:return
/self->trace == 1/
{
    @stacks[ustack(10)] = count();
    
    self->trace = 0;
    self->size = 0;
}

上面就是 probe 了 jemalloc 的 mallocx 和 reallocx 兩個函數,@stacks[ustack(10)] = count() 這個是記錄次數,如果想記錄 mem 總的 size,可以使用 @stacks[ustack(10)] = sum(self->size)

我們仍然可以通過 Flamegraph 工具以及 gprof2dot 工具來生成火焰圖和 Dot Graph,命令如下:

iconv -f ISO-8859-1 -t UTF-8 out.mem_stacks| ./gprof2dot.py -f dtrace | dot -Tsvg -o mem_graph.svg
./FlameGraph/stackcollapse.pl < out.mem_stacks | ./FlameGraph/flamegraph.pl --color=mem  > mem.svg

大概生成的樣子如下:

具體大家可以參考 這篇文章 在 Mac 上面安裝使用相關的工具。悲催的是,DTrace 雖然強大,但 Mac 最近的版本,引入了更強大的安全機制 - System Integrity Protection (SIP) ,導致很多 DTrace 的功能不能使用,所以我們要顯示的關掉 SIP,可以參考 這篇文章,幸運的是,上面提到的 CPU 和 Memory 的 tracing 是能直接使用的,所以並不需要關閉 SIP。

總結

上面只是說了下如何在 Mac 上面進行性能調優的手段,這已經能解決我們很多的問題了,但考慮到 Linux 和 Mac 在一些地方不一樣,譬如網絡編程上面,epoll 和 kqueue 的區別這些,所以我們仍然需要掌握在 Linux 上面調優的方法。

如果大家仔細觀察,其實會發現,無論是 DTrace,還是 SystemTap,甚至是 eBPF,它們都非常類似,所以你非常容易就能掌握這些工具,無論是 Mac 還是 Linux,對於性能問題,都能遊刃有餘的處理。

不過寫到最後,我愈發的喜歡 Go 了,原生提供了 pprof 這個強大的工具,何必像 Rust 這麼折騰了。。。

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