perfとflamegraphを使ってプロファイリング

# 概要
* この記事では、プロファイリングツールであるperfまたはdtraceと、可視化ツールであるflamegraph-rsを使ったRustプログラムのパフォーマンス解析の手順をまとめています
* flamegraphを使うと、プロファイリングツールを使って記録されたスタックトレースの情報をグラフとして可視化することができます
* flamegraph-rsは特に使いやすく、cargoを通してインストールするだけでflamegraphの生成ができるようになります

# 環境
対象のOS: Ubuntu22.04, Windows 11
言語: Rust

:::note warn
macOSでプロファイリングを行う場合は、SIP(Security Integrity Protection)というセキュリティシステムを無効化する必要があるため、自己責任でお願いします。
:::

# プロファイラーの準備
## Ubuntuの場合
### perf
perfはlinux用のプロファイリングツールで、cpu使用率やメモリ占有率、スタックトレース等を記録することができる。

Ubuntuにperfをインストールする場合は、下記のコマンドを入力する。
```bash
sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r`
```
wsl2にもperfをインストールすることができるが、プロファイリングしても特に有益な情報は得られないため(ハードウェアからの情報を得られない)、今回は省く。

## Windowsの場合
### dtrace
Windowsに対応しているコマンドラインプロファイラーツール。
perfはWindowsに対応していないため、こちらを代わりにインストールする必要がある。

1. PowerShellを管理者権限で実行して、下記のコマンドを入力:
    ```PowerShell
    bcdedit /set dtrace on
    ```
    
2. dtrace installerを次のリンクからダウンロードし、実行する
https://download.microsoft.com/download/7/9/d/79d6b79a-5836-4118-a9b7-60bc77c97bf7/DTrace.amd64.msi

3. 再起動する

([DTrace on Windows](https://github.com/microsoft/DTrace-on-Windows)より引用)

<!--
https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13
-->

## macOSの場合
dtraceはデフォルトでインストールされているが、SIPによってrootユーザでもsyscallへのアクセスを制限されているため、そのままプロファイリングを行っても有益な情報が得られない。

https://itochu-cable-systems.jp/esb/category/useful/other/2023/10/6653/
> 1. PCの電源を入れた後にcommand＋Rを長押しする
> 2. 自動的にリカバリーモードに入る
> 3. メニューバーのユーティリティー ⇒ ターミナルを選択する
> 4. コマンド【csrutil disable】を入力する
> 5. 以下のメッセージが表示されたらPCを再起動する
> System Integrity Protection is off.
> Restart the machine for the changes to take effect.
> ※SIPを有効化に戻す場合は、4.の手順で【csrutil enable】を入力する
# flamegraphとは
Brendan Greggs氏が開発したスタックトレースを可視化するソフトウェア。
縦軸がスタックトレースの深さ、横軸が実行時間となっていて、実際にCPU上で実行されている関数を確認するためには一番上を確認する。

生成されるグラフはsvgファイルフォーマットとなっており、ブラウザで開くとズーム機能やサーチ機能を使うことができ、特定の関数のパフォーマンスを見る場合に便利。

[Brendan Gregg氏のホームページ](https://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html)より引用
![cpu-bash-flamegraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518918/2562ea06-e9f4-61fd-0e0b-672f56838497.png)

# flamegraph-rs
今回はcargoから簡単にインストールできる[flamegraph-rs](https://github.com/flamegraph-rs/flamegraph)を使っていきます。
 
```bash
cargo install flamegraph
```
例として、以下のプログラムをプロファイリングします。

任意のディレクトリで
```bash
cargo new --bin test-flamegraph
```
として、src/main.rsを以下のように変更します。

```Rust: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に下記の記述を追加する。
```toml:Cargo.toml
[profile.release]
debug = true
```
とするとデバッグ情報を残したまま最適化してくれるようになるので、グラフが読みやすくなる。

最後に
```bash
cargo flamegraph --root -F 100
```
と入力すると、プロファイル結果をグラフ化したflamegraph.svgが出力される。

出力されたファイルはブラウザーで開くと、下のような画像が見れる。

![flamegraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518918/07ce3099-e573-e38a-8254-bd52fa2bb07f.png)
(main関数以外は使っていないため、rust側の情報はかなり少ない...)


# 参考
1. https://github.com/flamegraph-rs/flamegraph
2. Brendan Greg, https://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html 
3. microsoft, https://github.com/microsoft/DTrace-on-Windows
4. https://itochu-cable-systems.jp/esb/category/useful/other/2023/10/6653/