Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

hsawada
zozotech
70億人のファッションを技術の力で変えていく
https://tech.zozo.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away