Edited at

Signposts と Instruments でパフォーマンスを計測する:基礎編

Signposts は Xcode 10 で導入されたパフォーマンスを計測するためのマーカーです。この記事では Signposts を実際に活用する人のために、Signposts でできることをまとめておきます。初心者用の Signpost の使い方についてはこちらの記事をご覧下さい。

Signposts と Instruments でパフォーマンスを計測する:入門編

https://qiita.com/hsawada/items/e027b85480c8778f154a


処理の時間を測る

パフォーマンス計測用のマーカーなので、まずは処理の時間を測るのが基本です。

let logSomething = OSLog(subsystem: "com.example.your-app", category: "Something")

os_signpost(.begin, log: logSomething, name: "fetch something")
fetchSomething()
os_signpost(.end, log: logSomething, name: "fetch something")

ログのハンドルを取得して、計測したい箇所を os_signpost().begin.end で囲みます。


非同期処理の時間を測る

上記の fetchSomething() が非同期に何度も呼ばれると、 .begin.end が対応しなくなり、正しく計測できなくなってしまいます。そういう時は OSSignpostID() を使って、ID を作成し、対応する .begin.end に同じ ID を指定するようにします。

let spid = OSSignpostID(log: logSomething)

os_signpost(.begin, log: logSomething, name: "fetch something", signpostID: spid)
fetchSomething()
os_signpost(.end, log: logSomething, name: "fetch something", signpostID: spid)

ただ、これだと、 ID の管理が面倒になることが多いので、オブジェクトから ID を取得する方法を取るのが便利だと思います。

let spid = OSSignpostID(log: logSomething, object: element)

オブジェクトごとに ID が取得できるので、下記のような使い方ができます。

let logSomething = OSLog(subsystem: "com.example.your-app", category: "Something")

for element in panel.elements {
let spid = OSSignpostID(log: logSomething, object: element)
os_signpost(.begin, log: logSomething, name: "fetch something", signpostID: spid)
fetchSomethingAsync() {
os_signpost(.end, log: logSomething, name: "fetch something", signpostID: spid)
}
}


メタデータを記録する

時間を測ると同時にその時に必要なメタデータを記録することもできます。例えば、イメージのダウンロードあれば、そのイメージのサイズなどをログとして記録することができます。

os_signpost(.begin, log: logSomething, name: "fetch something", "%d %.1f", x1, y1)

フォーマットさえきちんと指定すれば、いろいろなタイプでたくさんの引数を指定することが可能です。

ここでは簡単な例を示すために、数値を並べていますが、実際に使用する時には、それぞれの数値の説明も含めて記録しておくべきです。後で分析する時に何の数値の羅列かを、コードと照らし合わせなければ、分からなくなってしまうからです。


イベントを記録する

パフォーマンスを分析する時、処理にかかる時間ばかりでなく、何かのイベントを記録したいこともよくあります。そんな時には os_signpost().event を指定します。

os_signpost(.event, log: logSomething, name: "tap something")

タップやスワイプなどのユーザー操作や、ネットワークコネクション確立後のイメージのダウンロード開始など、いわゆる普通のログと同じように、パフォーマンス分析において、興味のあるイベントをこれで記録します。


マーカーを無効にする

Signposts は非常に軽量で、アプリのリソースに影響はほとんど与えないそうです。しかしながら、限られた場所に大量にマーカーがあって、その影響が懸念される場合や、一部のマーカーを無効にして、分析しやすくしたい場合には、カテゴリーごとにマーカーを無効化できます。

マーカーのカテゴリーは、ログのハンドルを取得する際に指定しています。

let logSomething = OSLog(subsystem: "com.example.your-app", category: "Something")

このログのハンドルに対して、 OSLog.disabled をセットすると、このハンドルを使用するマーカーは全て動かなくなります。つまり、このカテゴリーのマーカーは全て動かなくなるわけです。

logSomething = .disabled

したがって、マーカーを入れていく際には、ログをどのようにカテゴリー分けするかについて、よく考慮しておいた方が良いと思います。パフォーマンスのマーカーを使い始めると、至る所に入れまくってしまい、いざ分析するときに注目したいデータが埋もれてしまうことがよくあるからです。適切にカテゴリー分けをしておけば、後々の管理がずっと楽になると思います。


計測専用のコードを分離する

上記のマーカーの無効化に関係しますが、計測専用のコードを書く場合には、ログハンドルの .signpostsEnabled を使って、通常のコードから分離しておいた方がいいと思います。

if logSomething.signpostEnabled {

fetchMetadata()
}

例えば、画像のファイルサイズの取得など、記録したいメタデータを取得するためだけにコードを書く場合には、このようにして通常コードと分離しておけば、そのカテゴリーが無効の時に、メタデータ取得のコードは実行されなくなります。


まとめ

Signposts を使うと、処理の継続時間やイベントを記録でき、それと同時に任意のメタデータも記録できます。また、状況に応じて、カテゴリごとにマーカーや計測用のコードを無効化することもできます。


参考文献

Measuring Performance Using Logging

https://developer.apple.com/videos/play/wwdc2018/405