cargo-profiler を Mac で使う

AtCoderのマラソンコンテストに際して、Rustコードのプロファイルを [cargo-profiler](https://github.com/svenstaro/cargo-profiler) を使用して取ろうとしたのですが、

> NOTE: This subcommand can only be used on Linux machines.

とある通りLinuxにしか対応していなかったので、Docker経由で使用しました。

その手順を書きます。

# 手順
## Dockerイメージのビルド
```
$ docker build -t cargo-profiler .
```

コマンドを、下記の Dockerfile を設置したディレクトリで実行する。


```Dockerfile:Dockerfile
# cf. https://github.com/rust-lang/docker-rust/blob/a035f6fa1f14754bab8f34376c386fafc831b652/1.49.0/buster/Dockerfile

FROM buildpack-deps:buster

ENV RUSTUP_HOME=/usr/local/rustup \
    CARGO_HOME=/usr/local/cargo \
    PATH=/usr/local/cargo/bin:$PATH \
    RUST_VERSION=1.49.0

RUN set -eux; \
    dpkgArch="$(dpkg --print-architecture)"; \
    case "${dpkgArch##*-}" in \
        amd64) rustArch='x86_64-unknown-linux-gnu'; rustupSha256='ed7773edaf1d289656bdec2aacad12413b38ad0193fff54b2231f5140a4b07c5' ;; \
        armhf) rustArch='armv7-unknown-linux-gnueabihf'; rustupSha256='7a7b9d246ad63358705d8d4a7d5c2ef1adfec24525d1d5c44a7739e1b867e84d' ;; \
        arm64) rustArch='aarch64-unknown-linux-gnu'; rustupSha256='f80a0a792b3ab905ab4919474daf4d3f60e574fc6987e69bfba2fd877241a8de' ;; \
        i386) rustArch='i686-unknown-linux-gnu'; rustupSha256='4473c18286aa1831683a772706d9a5c98b87a61cc014d38063e00a63a480afef' ;; \
        *) echo >&2 "unsupported architecture: ${dpkgArch}"; exit 1 ;; \
    esac; \
    url="https://static.rust-lang.org/rustup/archive/1.23.1/${rustArch}/rustup-init"; \
    wget "$url"; \
    echo "${rustupSha256} *rustup-init" | sha256sum -c -; \
    chmod +x rustup-init; \
    ./rustup-init -y --no-modify-path --profile minimal --default-toolchain $RUST_VERSION --default-host ${rustArch}; \
    rm rustup-init; \
    chmod -R a+w $RUSTUP_HOME; \
    rustup --version; \
    cargo --version; \
    rustc --version;

RUN apt-get update \
    && apt-get install -y valgrind

RUN cargo install cargo-profiler \
    && chmod -R a+w $CARGO_HOME
```

（このファイルは、https://github.com/rust-lang/docker-rust/blob/a035f6fa1f14754bab8f34376c386fafc831b652/1.49.0/buster/Dockerfile の末尾に

```Dockerfile
RUN apt-get update \
    && apt-get install -y valgrind

RUN cargo install cargo-profiler \
    && chmod -R a+w $CARGO_HOME
```

を書き加えたものになります。</br>

AtCoderのジャッジサーバのバージョンに合わせて、Rust v1.49.0を使っています。）

## cargoプロジェクトのビルド
```
$ docker run --rm --user "$(id -u)":"$(id -g)" \
    -v /your/cargo/project/path:/workspace \
    -w /workspace cargo-profiler \
    cargo build --release
```

（`/your/cargo/project/path` には、プロファイル対象のcargoプロジェクトのルートディレクトリを記載してください。）

## プロファイル実行
```
$ docker run --rm --user "$(id -u)":"$(id -g)" \
    -v /your/cargo/project/path:/workspace \
    -w /workspace cargo-profiler \
    cargo profiler callgrind --bin target/release/a -n 5
```

(`target/release/a` 部分は実行ファイルパスを入れてください。)

以下のような結果が得られます。

```
Profiling a with callgrind...

Total Instructions...7,131,502,099

1,228,544,445 (17.2%) ???:a::State
-----------------------------------------------------------------------
1,067,368,888 (15.0%) malloc.c:_int_free'2
-----------------------------------------------------------------------
1,017,529,165 (14.3%) ???:<alloc::vec
-----------------------------------------------------------------------
675,019,392 (9.5%) ???:a::State
-----------------------------------------------------------------------
462,097,916 (6.5%) malloc.c:_int_malloc'2
-----------------------------------------------------------------------
```

`callgrind` の他に、`cachegrind` というコマンドもあるようです（私は使ったことないです）。

# 余談
- [Macでも使えたという記事](https://scrapbox.io/nwtgck/Rustのプロジェクトでボトルネックを探し出す)も見つけたのですが、私の環境下では動きませんでした。OSバージョンとかによるんですかね？[^1]
- [macos-profiler](https://github.com/tcr/macos-profiler) という MacOS 向けの
Rustプロファイラもありましたが、私は動かせませんでした。最終コミットが'19年5月だったので、私の環境だと非対応って話な気がしてます。
- [標準入力が渡せないっぽい](https://github.com/svenstaro/cargo-profiler/issues/37)ので、競プロ用途だとちょっと手間[^2]だったりします。私は `cargo-profiler` を使うときだけ、コードを入力をファイルから読み込むように書き換えて使っています。

[^1]: 私の環境は Apple M1, Big Sur って感じです。
[^2]: 競技プログラミングコンテストは、入力を標準入力から受け取る形式が多いため