【iOS】Metal System Traceを使ってMetalアプリの処理時間を計測する方法

InstrumentsのMetal System TraceのMetalを使ったアプリの処理時間を詳細に分析することができます。
この記事ではそのやり方を説明します。

### 今回計測するアプリ

GitHubに[TokyYoshida/MetalExamples](https://github.com/TokyoYoshida/MetalExamples)というMetalのサンプルコードを集めたリポジトリを作りました。今回はこの中の[Particle](https://github.com/TokyoYoshida/MetalExamples/tree/main/MetalExamples/Samples/Particle)というサンプルを使用します。

パーティクルが上から下に流れるだけの処理です。
なお、このアプリはステータスバーにFPSが表示されているので、いまどのぐらいのFPSがでているのかすぐに確認できるようになっています。

(実行イメージ)
<img width="200px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/0a662430-3b57-8186-d023-9c4608cf5603.gif">

実はパーティクルはハートマークでしかも回転しているのですが、小さすぎて分かりません😇

### 計測の手順

それでは計測してみます。

**1. XcodeからMetal System Traceを起動する**

Xcode > Open Developer Tool > InstrumentsでInstrumentsを起動し、さらにMetal System Traceを選択します。

**2. パーティクルの数を決めてアプリを実行する**

パーティクルの数を決めてアプリを実行します。

パーティクルはこの部分の`instanceCount`に指定する値で決まります。
まずは100万を指定してみましょう。私のiPhone 11だと45FPSが出ていました。


```swift:ParticleMetalView.swift
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 100000)

```

ビルドして、アプリを実行します。

**3. Metal System Traceで計測を開始する**

アプリを実行してパーティクルを表示した状態でMetal System Traceに戻り、左上のボタンを押して計測を開始します。

<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/c1ffa741-dfcf-73b4-9988-a2d95c9230ee.png">

**4. 計測結果をみる**

適当なところで■ボタンを押すと計測が止まります。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/957d3990-3ed4-8b24-ed3c-326aa2d21750.png">

結果を見てみましょう。

＜図１＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/89692b8f-cd99-557a-7b98-4df6e0a3ce28.png)
アプリケーション（CPU)の処理、GPUの処理、ディスプレイのインスタンスの処理間隔が表示されます。今回なぜかvsyncが表示されなかったので、手書きで入れてみました。

vsyncは16.67ms間隔になっているので、60FPSを出すためにはこの範囲内でCPUとGPUの処理を終わらせている必要があります。

下のペインにはBuilt-in Displayの1つ1つの処理がリスト化されています。みると16.67msで終わっているものもあれば、33msかかっているものもあります。このように処理時間がかかるとフレームがスキップされて16.67のn倍の時間がかかってしまいます。

**5. 処理の内訳を確認する**

原因を分析するためには、遅くなっている処理の内訳を確認していきます。
上の図１をみるとCPUの処理は間に合っていますが、GPUの処理が大幅に遅くなっています。

ということで、GPUの処理の内訳を見ていきます。
使用しているiPhoneのチップの名前(この記事の場合はA13)の▶をクリックすると処理の内訳をみることができます。

<img width="800px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/d39cf1c9-7c26-72f5-7caf-44c0a0a5c4ea.png">

処理は、フレームごとに色分けされていて次の順番で処理していることがわかります。

MetalExamples　・・・ CPUの処理
=> Driver Processing ・・・ GPUドライバの処理
=> Vertex ・・・　頂点の変換や頂点シェーダーの実行
=> Fragment　・・・　ラスタライズ、フラグメント シェーダーの実行
=> Direct to Display ・・・　描画

図中にコメントを書きましたが、Direct to Displayより前の処理がvsyncに間に合わないと、現在の画面を表示し続けることになるためFPSのレートが落ちます。緑色で示されたフレームの場合、特にVertexとFragmentの処理に時間がかかりすぎており、描画がvsyncに間に合っていないことがわかります。

**6. パーティクルの数を減らして処理を観測する**

パーティクルの数を10万にしてさきほどのグラフを出してみましょう。
この場合、私のiPhoneでも60FPSが出ていました。

<img width="800px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116010/1d0d2860-cb32-088b-f648-7edd600a2079.png">

すべての処理がvsyncに収まっていることがわかります。

### 最後に

iOSを使ったARやML、音声処理などの作品やサンプル、技術情報を発信しています。
作品ができたらTwitterで発信していきますのでフォローをお願いします🙏

Twitterは作品や記事のリンクを貼っています。
https://twitter.com/jugemjugemjugem

Qiitaは、iOS開発、とくにARや機械学習、グラフィックス処理、音声処理について発信しています。
https://qiita.com/TokyoYoshida

Noteでは、連載記事を書いています。
https://note.com/tokyoyoshida

Zennは機械学習が多めです。
https://zenn.dev/tokyoyoshida