Swiftでの時間計測の完全ガイド!実用的な方法7選

時間計測の方法に関するアイキャッチ画像Swift
この記事は約19分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

あなたはSwiftでアプリケーションを開発していて、「この処理、もう少し速くならないかな?」と思ったことはありませんか?

それとも、ある処理のパフォーマンスを正確に測りたい、でも正確な方法がわからないと困っていませんか?

この記事を読めば、Swiftでの時間計測を手軽に始めることができるようになります。

私たちは常にコードの効率を追求しています。

その中で、時間計測はコードのパフォーマンスを評価、改善するために欠かせないスキルです。

今回はSwiftを使った時間計測の基礎から応用、そしてカスタマイズ方法までを丁寧にご紹介します。

●Swiftと時間計測の基礎

○Swiftとは?

Swiftは、Appleが開発したプログラミング言語です。

iOS, macOS, watchOS, tvOSなど、Appleの各プラットフォームでのアプリ開発に使われています。

Swiftは、その安全性と効率性から、多くの開発者に愛されている言語です。

○時間計測の重要性

時間計測は、アプリ開発において非常に重要なステップです。

パフォーマンスの最適化、ボトルネックの特定、改善策の効果測定など、様々なシーンで時間計測は欠かせません。

特に、ユーザー体験を向上させるためには、アプリのレスポンスタイムを最適に保つことが必須です。

それには、正確な時間計測が必要となります。

Swiftでの時間計測は、いくつかの方法がありますが、その中でもCFAbsoluteTimeGetCurrent()関数を用いた方法が一般的です。

これは、シンプルで正確、そしてわかりやすい方法です。

アプリケーションがユーザーに迅速なレスポンスを提供するためには、コードの各部分の実行時間を把握し、必要に応じて最適化を行う必要があります。

それには、正確な時間計測が欠かせません。

それでは、具体的なサンプルコードと共に、Swiftでの時間計測の方法を学んでいきましょう。

この記事では、Swiftの基本から時間計測のテクニック、さらにはそれを応用したカスタマイズ方法までを、具体的なコード例を交えて解説していきます。

それぞれのコードは、その場で試してもらえるように、簡潔かつわかりやすいものを選んでいます。

●Swiftでの時間計測の使い方

Swiftでの時間計測は、コードの効率を評価するために重要なスキルです。

特定の処理がどれほどの時間を要するのか、または2つの異なる方法で同じタスクを実行した場合にどちらが速いのかを知ることができます。

ここでは、Swiftでの時間計測の基本的な使い方から、いくつかの実用的なサンプルコードを通じてその手法を学んでいきます。

○サンプルコード1:基本的な時間計測

このコードではCFAbsoluteTimeGetCurrent関数を使って、特定の処理の実行時間を計測する基本的な方法を紹介しています。

この例では、1000000回のループ処理の実行時間を計測しています。

import Foundation

// 開始時間の取得
let startTime = CFAbsoluteTimeGetCurrent()

// 何らかの処理
for _ in 0..<1000000 {
    // ここに時間を計測したい処理を書く
}

// 終了時間の取得
let endTime = CFAbsoluteTimeGetCurrent()

// 実行時間の計算
let runTime = endTime - startTime

// 結果の表示
print("実行時間: \(runTime)秒")

実行すると、”実行時間: 〇〇秒”という形式で、ループ処理の実行時間が表示されます。

この時間は実行するたびに微妙に異なることがありますので、何度か実行して平均をとることがおすすめです。

○サンプルコード2:処理時間の差を計測

次に、2つの異なる方法で同じタスクを実行し、それぞれの実行時間を計測する方法を見ていきましょう。

この例では、配列のソート処理を2つの異なる方法で行い、それぞれの実行時間を計測しています。

import Foundation

let array = (1...1000000).map { _ in Int.random(in: 0...1000000) }

// 方法1の時間計測
let startTime1 = CFAbsoluteTimeGetCurrent()
let sortedArray1 = array.sorted()
let endTime1 = CFAbsoluteTimeGetCurrent()
let runTime1 = endTime1 - startTime1

// 方法2の時間計測(例として同じsortedメソッドを使用)
let startTime2 = CFAbsoluteTimeGetCurrent()
let sortedArray2 = array.sorted { $0 > $1 }
let endTime2 = CFAbsoluteTimeGetCurrent()
let runTime2 = endTime2 - startTime2

print("方法1の実行時間: \(runTime1)秒")
print("方法2の実行時間: \(runTime2)秒")

このコードを実行すると、2つのソート方法それぞれの実行時間が表示されます。

こうして、どちらの方法が効率的かを比較することができます。

もちろん、実際のアプリケーションでは、異なる処理方法やアルゴリズムの選択によって、計測結果が大きく異なることもあります。

○サンプルコード3:複数の処理を比較

実際のアプリケーション開発において、複数の処理方法が考えられる場面はよくあります。

それぞれの処理方法がどれほどの時間を要するのか比較することで、より効率的な方法を選択するための判断材料とすることが可能です。

このコードでは、3つの異なるアルゴリズムを使用して配列の要素の合計を求め、それぞれの処理時間を比較しています。

import Foundation

let array = (1...1000000).map { _ in Int.random(in: 0...1000000) }

// 方法1:for-inループを使用
let startTime1 = CFAbsoluteTimeGetCurrent()
var sum1 = 0
for value in array {
    sum1 += value
}
let endTime1 = CFAbsoluteTimeGetCurrent()
let runTime1 = endTime1 - startTime1

// 方法2:reduceメソッドを使用
let startTime2 = CFAbsoluteTimeGetCurrent()
let sum2 = array.reduce(0, +)
let endTime2 = CFAbsoluteTimeGetCurrent()
let runTime2 = endTime2 - startTime2

// 方法3:compactMapとreduceを組み合わせて使用
let startTime3 = CFAbsoluteTimeGetCurrent()
let sum3 = array.compactMap { $0 }.reduce(0, +)
let endTime3 = CFAbsoluteTimeGetCurrent()
let runTime3 = endTime3 - startTime3

print("方法1での合計: \(sum1), 実行時間: \(runTime1)秒")
print("方法2での合計: \(sum2), 実行時間: \(runTime2)秒")
print("方法3での合計: \(sum3), 実行時間: \(runTime3)秒")

実行すると、各方法での計算結果とその実行時間が出力されます。

この例では、同じタスクに対する異なるアプローチによる時間の違いを視覚的に把握することができます。

なお、このコードを複数回実行することで、各方法の平均的な実行時間も知ることができます。

○サンプルコード4:リアルタイムでの時間計測表示

開発中のアプリで、リアルタイムでの時間計測が必要な場面も考えられます。

特に、ユーザーの操作に対する応答時間やアニメーションの完了時間などを計測する際には、このようなリアルタイムの計測が有効です。

このコードでは、Timerクラスを使用して、1秒ごとに経過時間を表示しています。

import Foundation

class TimeMeasure {
    private var startTime: Date?
    private var timer: Timer?

    func start() {
        self.startTime = Date()
        self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
    }

    func stop() {
        self.timer?.invalidate()
        self.timer = nil
    }

    @objc private func update() {
        guard let startTime = self.startTime else { return }
        let elapsedTime = Date().timeIntervalSince(startTime)
        print("経過時間: \(elapsedTime)秒")
    }
}

let measure = TimeMeasure()
measure.start()

// ここで何らかの処理を行う
// ...

measure.stop()

上記のコードを実行すると、開始からの経過時間が1秒ごとにコンソールに表示されます。

特定の処理がどれほどの時間を要しているのか、リアルタイムで確認することができます。

この方法は、デバッグ時やパフォーマンスのチューニング時に非常に役立ちます。

●Swiftでの時間計測の応用例

Swiftでの時間計測は基本的な計測から応用的な計測まで幅広く使用されます。

実践的な場面での計測方法や、その際のベストプラクティスを知ることで、効率的なコードの最適化や、パフォーマンス改善のための重要な手助けとなります。

ここでは、Swiftにおける時間計測の応用的な使用方法をいくつか取り上げ、具体的なサンプルコードと共に説明します。

○サンプルコード5:特定の処理間の時間計測

このコードでは、特定の処理間だけの時間計測を行う方法を表しています。

特定のタスク間の時間を計測することで、該当部分のパフォーマンスを細かく把握することが可能です。

import Foundation

func sampleFunction() {
    for _ in 0..<10000 {
        print(".")
    }
}

let startTime = CFAbsoluteTimeGetCurrent()
sampleFunction()
let endTime = CFAbsoluteTimeGetCurrent()

let elapsedTime = endTime - startTime
print("特定の処理にかかった時間は、約\(elapsedTime)秒です。")

この例では、sampleFunctionの実行時間を計測しています。

この方法を使用すると、アプリケーションの特定の部分の実行時間を正確に把握することができます。

○サンプルコード6:コードの最適化前後での時間計測

コードの最適化やリファクタリングを行う際、その改善前後でのパフォーマンスの変化を計測することは非常に有益です。

下記のコードでは、最適化前と最適化後の関数の実行時間を比較しています。

import Foundation

func nonOptimizedFunction() {
    var result = 0
    for i in 0..<10000 {
        result += i
    }
}

func optimizedFunction() {
    let result = (0..<10000).reduce(0, +)
}

let startTime1 = CFAbsoluteTimeGetCurrent()
nonOptimizedFunction()
let endTime1 = CFAbsoluteTimeGetCurrent()

let startTime2 = CFAbsoluteTimeGetCurrent()
optimizedFunction()
let endTime2 = CFAbsoluteTimeGetCurrent()

let elapsedTime1 = endTime1 - startTime1
let elapsedTime2 = endTime2 - startTime2

print("最適化前の関数の実行時間は、約\(elapsedTime1)秒です。")
print("最適化後の関数の実行時間は、約\(elapsedTime2)秒です。")

上記の結果を確認すると、optimizedFunctionの方がnonOptimizedFunctionよりも高速に動作していることが分かります。

このようにして、改善の効果を実際の数字で把握することができます。

○サンプルコード7:ユーザーインターフェースとの連携

アプリケーションのユーザーインターフェース(UI)との連携で時間計測を行う場合もあります。

例えば、ユーザーがボタンを押すタイミングや、アニメーションの完了時間などを計測することで、ユーザビリティの向上に役立てることができます。

import UIKit

class SampleViewController: UIViewController {
    var startTime: Date?

    @IBOutlet weak var startButton: UIButton!
    @IBOutlet weak var stopButton: UIButton!

    @IBAction func startButtonTapped(_ sender: UIButton) {
        self.startTime = Date()
    }

    @IBAction func stopButtonTapped(_ sender: UIButton) {
        guard let startTime = self.startTime else { return }
        let elapsedTime = Date().timeIntervalSince(startTime)

        let alert = UIAlertController(title: "計測結果", message: "経過時間:約\(elapsedTime)秒", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

このコードでは、ユーザーが「開始」ボタンを押してから「終了」ボタンを押すまでの時間を計測し、その結果をアラートで表示しています。

このようにして、UIの操作に関する情報を収集し、それを元にUIの改善や最適化を行うことができます。

●注意点と対処法

Swiftにおける時間計測は、多くの場面での最適化やパフォーマンスの向上のための有力なツールとして活用されます。

しかし、正確かつ効果的な計測を行うためには、いくつかの注意点やベストプラクティスを知っておくことが重要です。

○計測の精度に関する注意

時間計測を行う際の最も基本的な注意点は、計測の精度です。

具体的なコードや手法を使用する前に、計測の精度に関する基本的な理解を深めることが必要です。

このコードでは、DateCFAbsoluteTimeGetCurrentを使用して計測の精度を比較します。

import Foundation

let start1 = Date().timeIntervalSince1970
Thread.sleep(forTimeInterval: 1.0)
let end1 = Date().timeIntervalSince1970

let start2 = CFAbsoluteTimeGetCurrent()
Thread.sleep(forTimeInterval: 1.0)
let end2 = CFAbsoluteTimeGetCurrent()

let elapsedTime1 = end1 - start1
let elapsedTime2 = end2 - start2

print("Dateを使用した計測の結果、約\(elapsedTime1)秒経過しました。")
print("`CFAbsoluteTimeGetCurrent`を使用した計測の結果、約\(elapsedTime2)秒経過しました。")

上記のコードを実行すると、CFAbsoluteTimeGetCurrentを使用した方がより高精度で計測できることが確認できます。

このように、使用する計測手法によって精度が変わるため、適切な手法を選択することが重要です。

○複数のデバイスでの差異

Swiftを使用したアプリケーションは、様々なデバイスや環境で実行される可能性があります。

そのため、同じコードでも異なるデバイス間での実行時間が変わることが考えられます。

import Foundation

let startTime = CFAbsoluteTimeGetCurrent()

// 何らかの処理

let endTime = CFAbsoluteTimeGetCurrent()

let elapsedTime = endTime - startTime

print("このデバイスでの処理にかかった時間は、約\(elapsedTime)秒です。")

この例を複数のデバイスで実行すると、それぞれのデバイスの性能や状態に応じて結果が異なる可能性があります。

このような場合、時間計測の結果を一概に解釈するのではなく、環境や条件を考慮して解釈することが重要です。

○計測結果の解釈の注意点

時間計測の結果は、単純に数字としての意味だけではなく、背景や状況に応じて解釈する必要があります。

例えば、同じタスクの実行時間が異なる場合、外部の要因や他のプロセスの影響を受けている可能性が考えられます。

import Foundation

func task() {
    for _ in 0..<10000 {
        print(".")
    }
}

let startTime = CFAbsoluteTimeGetCurrent()
task()
let endTime = CFAbsoluteTimeGetCurrent()

let elapsedTime = endTime - startTime

print("タスクの実行にかかった時間は、約\(elapsedTime)秒です。")

このコードの実行結果を解釈する際には、他のアプリケーションの動作やデバイスの状態など、多くの要因を考慮することが求められます。

計測結果を適切に解釈し、それに基づいて最適化や改善を行うことが、効果的な時間計測の鍵となります。

●カスタマイズ方法

Swiftでの時間計測は、その結果を効果的に表示し、計測方法自体もカスタマイズすることが可能です。

ここでは、計測結果のビジュアル表示や、カスタムした時間計測関数の作成方法について解説します。

○時間計測の結果をビジュアル表示

時間計測の結果を単に数字として出力するだけでなく、グラフやバーとしてビジュアルに表示することで、直感的に処理の遅延やその変動を捉えることができます。

このコードでは、SwiftUIを利用して、時間計測の結果をバーとして表示します。

この例では、3つの異なるタスクの計測結果をバーとして表示しています。

import SwiftUI

struct TimeMeasurementView: View {
    // タスクごとの計測結果
    let taskTimes: [Double] = [0.5, 1.0, 0.8]

    var body: some View {
        VStack(alignment: .leading) {
            ForEach(taskTimes, id: \.self) { time in
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: CGFloat(time * 100), height: 20) // 時間の長さに応じてバーの幅を変更
                    .padding(.bottom, 5)
            }
        }
    }
}

struct TimeMeasurementView_Previews: PreviewProvider {
    static var previews: some View {
        TimeMeasurementView()
    }
}

上記のコードを実行すると、3つの異なるタスクの計測結果がバーとして表示されます。

このように、ビジュアルに表示することで、一目でどのタスクが時間がかかっているのかを確認できるようになります。

○カスタム時間計測関数の作成

Swiftでは、自分だけのカスタム時間計測関数を作成することも可能です。

これにより、特定のニーズや要件に合わせて時間計測を行うことができます。

このコードでは、処理の開始と終了時刻を引数として受け取り、その間の経過時間を計算するカスタム関数を作成します。

import Foundation

// カスタム時間計測関数
func customTimeMeasurement(start: CFAbsoluteTime, end: CFAbsoluteTime) -> Double {
    return end - start
}

let startTime = CFAbsoluteTimeGetCurrent()

// 何らかの処理

let endTime = CFAbsoluteTimeGetCurrent()

let elapsedTime = customTimeMeasurement(start: startTime, end: endTime)

print("このタスクの実行にかかった時間は、約\(elapsedTime)秒です。")

このコードを利用することで、任意の処理の開始と終了時刻を基にして時間計測を行うことができます。

また、関数をカスタマイズすることで、計測結果のフォーマットや、結果に応じた特別な処理を追加することも可能です。

まとめ

Swiftにおける時間計測は、アプリケーションの性能向上や問題の特定に非常に役立ちます。

本ガイドでは、Swiftでの時間計測の基本的な方法から、実用的な応用例、注意点、さらにはカスタマイズ方法まで幅広く取り上げました。

初心者から上級者まで、それぞれのニーズに応じて時間計測の手法を選択し、適切に利用することが求められます。

特に、計測結果の正確な解釈や、異なるデバイスや状況での差異を理解することは、高品質なアプリケーションの開発に不可欠です。

また、ビジュアルによる結果の表示や、カスタム関数の導入により、より直感的かつ柔軟な時間計測が実現できることも確認しました。

最後に、Swiftでの時間計測は単なるツールではありません。

それを適切に活用することで、ユーザーエクスペリエンスの向上や、アプリケーションの効率的な最適化を実現できる強力な武器となるでしょう。

今後の開発活動において、本ガイドが皆様の参考となり、より高品質なアプリケーションの開発をサポートすることを心より願っています。