概要
- この記事では、プロファイリングツールであるperfまたはdtraceと、可視化ツールであるflamegraph-rsを使ったRustプログラムのパフォーマンス解析の手順をまとめています
- flamegraphを使うと、プロファイリングツールを使って記録されたスタックトレースの情報をグラフとして可視化することができます
- flamegraph-rsは特に使いやすく、cargoを通してインストールするだけでflamegraphの生成ができるようになります
環境
対象のOS: Ubuntu22.04, Windows 11
言語: Rust
macOSでプロファイリングを行う場合は、SIP(Security Integrity Protection)というセキュリティシステムを無効化する必要があるため、自己責任でお願いします。
プロファイラーの準備
Ubuntuの場合
perf
perfはlinux用のプロファイリングツールで、cpu使用率やメモリ占有率、スタックトレース等を記録することができる。
Ubuntuにperfをインストールする場合は、下記のコマンドを入力する。
sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r`
wsl2にもperfをインストールすることができるが、プロファイリングしても特に有益な情報は得られないため(ハードウェアからの情報を得られない)、今回は省く。
Windowsの場合
dtrace
Windowsに対応しているコマンドラインプロファイラーツール。
perfはWindowsに対応していないため、こちらを代わりにインストールする必要がある。
-
PowerShellを管理者権限で実行して、下記のコマンドを入力:
bcdedit /set dtrace on
-
dtrace installerを次のリンクからダウンロードし、実行する
https://download.microsoft.com/download/7/9/d/79d6b79a-5836-4118-a9b7-60bc77c97bf7/DTrace.amd64.msi -
再起動する
(DTrace on Windowsより引用)
macOSの場合
dtraceはデフォルトでインストールされているが、SIPによってrootユーザでもsyscallへのアクセスを制限されているため、そのままプロファイリングを行っても有益な情報が得られない。
- PCの電源を入れた後にcommand+Rを長押しする
- 自動的にリカバリーモードに入る
- メニューバーのユーティリティー ⇒ ターミナルを選択する
- コマンド【csrutil disable】を入力する
- 以下のメッセージが表示されたらPCを再起動する
System Integrity Protection is off.
Restart the machine for the changes to take effect.
※SIPを有効化に戻す場合は、4.の手順で【csrutil enable】を入力する
flamegraphとは
Brendan Greggs氏が開発したスタックトレースを可視化するソフトウェア。
縦軸がスタックトレースの深さ、横軸が実行時間となっていて、実際にCPU上で実行されている関数を確認するためには一番上を確認する。
生成されるグラフはsvgファイルフォーマットとなっており、ブラウザで開くとズーム機能やサーチ機能を使うことができ、特定の関数のパフォーマンスを見る場合に便利。
flamegraph-rs
今回はcargoから簡単にインストールできるflamegraph-rsを使っていきます。
cargo install flamegraph
例として、以下のプログラムをプロファイリングします。
任意のディレクトリで
cargo new --bin test-flamegraph
として、src/main.rsを以下のように変更します。
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::BufReader;
fn main() {
// create foo.txt and open it with read and write modes enabled
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("foo.txt")
.unwrap();
file.write_all(b"Hello, world!").unwrap();
// without buffered I/O
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
// with buffered I/O
let mut buf_reader = BufReader::new(file);
let mut contents_buf = String::new();
buf_reader.read_to_string(&mut contents_buf).unwrap();
assert_eq!(contents, contents_buf);
}
flamegraph-rsはデフォルトでcargo run --release
を実行して、その結果をプロファイリングするが、最適化の際に関数の情報まで省かれてしまうので、Cargo.tomlに下記の記述を追加する。
[profile.release]
debug = true
とするとデバッグ情報を残したまま最適化してくれるようになるので、グラフが読みやすくなる。
最後に
cargo flamegraph --root -F 100
と入力すると、プロファイル結果をグラフ化したflamegraph.svgが出力される。
出力されたファイルはブラウザーで開くと、下のような画像が見れる。
(main関数以外は使っていないため、rust側の情報はかなり少ない...)