Kotlinで経過時間を測定する12の方法

Kotlinで経過時間を測定するイラストとサンプルコードのスクリーンショットKotlin
この記事は約24分で読めます。

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

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

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

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

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

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

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

はじめに

この記事を読めば、Kotlinで経過時間を測定する方法を12通り身につけることができるようになります。

最近、アプリやウェブサイトのパフォーマンスを最適化するために、経過時間や処理時間を正確に測定することが求められるようになってきました。

Kotlinは現代の多くの開発者に利用されているプログラミング言語で、この言語を使用して経過時間を効率的に測定する方法を知ることは、パフォーマンスの最適化やデバッグ時に非常に役立ちます。

●Kotlinとは

Kotlinは、2011年にJetBrains社によって開発されたモダンなプログラミング言語です。

Javaとの相互運用性を持ちつつ、より簡潔で生産的にコードを書くことができるところが特徴です。

Androidの公式開発言語としても採用されており、多くの開発者が日常的に使用しています。

○Kotlinの特徴

Kotlinの特徴は、次の点に集約されます。

  1. Null安全:Kotlinは、Null参照を回避するための特別な仕組みを持っています。これにより、ランタイムでのNull参照エラーを大幅に削減することができます。
  2. Javaとの相互運用性:KotlinはJavaと完全に互換性があります。これにより、Javaで書かれたライブラリやフレームワークをそのまま利用することができます。
  3. 簡潔な構文:Kotlinは、読みやすく、書きやすい簡潔な構文を持っています。特に、ラムダ式や拡張関数などの機能を活用することで、より効率的なコードを実現することができます。
  4. スクリプト言語としても利用可能:Kotlinは、コンパイル言語であると同時に、スクリプト言語としても動作します。これにより、短いスクリプトやツールを簡単に書くことができます。

これらの特徴により、Kotlinは多くの場面で高い生産性を発揮します。

特に、経過時間を測定するような処理でも、Kotlinの強力な機能を活用することで、簡単かつ正確に実装を行うことができます。

●経過時間の測定の基本

経過時間の測定は、プログラムの実行時間を評価するための基本的な手法として利用されます。

特にアプリケーションのパフォーマンスチューニングや、ボトルネックの特定などに役立ちます。

Kotlinでも経過時間の測定は簡単に実行でき、そのためのメカニズムや仕組みを持っています。

○なぜ経過時間を測定するのか

経過時間の測定は次のようなシチュエーションで非常に有効です。

  1. パフォーマンス最適化:アプリケーションやウェブサイトの速度は、ユーザー体験に大きく影響します。長い読み込み時間や遅延は、ユーザーが離脱する原因となることが多いです。経過時間の測定を行うことで、どの部分が遅いのかを特定し、最適化の手がかりとすることができます。
  2. バグの特定:何らかの処理が予想よりも時間がかかっている場合、そこにはバグや非効率なコードが隠れている可能性があります。経過時間の測定により、そのような問題を発見する手助けとなります。
  3. リソースの節約:無駄に時間がかかる処理は、CPUやメモリなどのリソースも浪費する可能性があります。経過時間の短縮は、リソースの節約にも繋がります。

○Kotlinでの経過時間測定の基本メカニズム

Kotlinでは、経過時間の測定をサポートするための様々な関数やライブラリが提供されています。

System.currentTimeMillis()System.nanoTime()といった関数を使用することで、現在の時刻やナノ秒単位の時刻を取得できます。

これを利用して、ある処理の前後での時間の差を計算することで、経過時間を測定することができます。

具体的には、処理を開始する前の時間を取得し、処理が終了した後の時間を再度取得、その差を計算することで、処理にかかった時間を求めることができます。

●経過時間の測定の方法

経過時間を正確に計測することは、パフォーマンスの最適化や問題点の特定、さらにはアプリケーションの品質向上に不可欠です。

Kotlinにおける時間の測定方法はいくつか存在し、シチュエーションに応じて適切なものを選択することが求められます。

○サンプルコード1:基本的な経過時間の取得

Kotlinでの経過時間の測定の一番基本的な方法はmeasureTimeMillis関数を使用することです。

この関数は、ブロック内のコードの実行にかかった時間をミリ秒単位で返します。

fun main() {
    val elapsedTime = kotlin.system.measureTimeMillis {
        // 測定したい処理をここに記述
        for (i in 1..1000000) {
            print("")
        }
    }
    println("処理にかかった時間: $elapsedTime ミリ秒")
}

このコードでは、print("")を1,000,000回実行する処理の実行時間を計測しています。

measureTimeMillisはブロック内の処理を実行し、その処理にかかった時間をミリ秒単位で返します。

○サンプルコード2:関数の実行時間を計測する

特定の関数の実行時間を計測したい場合、次のようにして関数の実行前後で時間を取得し、その差を計算することで経過時間を知ることができます。

fun sampleFunction(): Int {
    // 何らかの計算
    return (1..100000).sum()
}

fun main() {
    val startTime = kotlin.system.nanoTime()

    val result = sampleFunction()

    val endTime = kotlin.system.nanoTime()
    val elapsedTime = (endTime - startTime) / 1_000_000.0
    println("sampleFunctionの実行時間: $elapsedTime ミリ秒")
}

このコードでは、sampleFunctionという関数の実行時間をナノ秒単位で計測しています。

関数の実行前後での時間をnanoTime関数を使って取得し、その差を用いて経過時間を計算します。

○サンプルコード3:ループ内の処理時間を計測する

プログラムの中でループは頻繁に使用されます。

特に大量のデータを扱う場合や繰り返しの計算を行う場合、ループ内の処理時間を知ることで全体のパフォーマンスの向上や問題の特定がしやすくなります。

Kotlinでは、ループ内の処理時間を簡単に測定することができます。

下記のサンプルコードでは、1から1,000,000までの数字を加算するループの実行時間を測定しています。

fun main() {
    val startTime = kotlin.system.nanoTime()

    var sum = 0L
    for (i in 1..1000000) {
        sum += i
    }

    val endTime = kotlin.system.nanoTime()
    val elapsedTime = (endTime - startTime) / 1_000_000.0
    println("ループ内の計算処理にかかった時間: $elapsedTime ミリ秒")
}

このコードでは、nanoTime関数を使用してループの開始前と終了後の時間を取得しています。

そして、その2つの時間の差を利用して、ループの実行にかかった経過時間を計算しています。

このようにして、任意のループ処理の実行時間をミリ秒単位で知ることができます。

○サンプルコード4:マルチスレッド環境での経過時間の計測

近年のコンピュータはマルチコアを持つものが多くなってきました。

そのため、マルチスレッドを使用して並列処理を行うことで、プログラムの実行速度を向上させることができます。

しかし、マルチスレッド環境では、各スレッドの実行時間や全体の実行時間を正確に計測することが重要です。

下記のサンプルコードでは、Kotlinのcoroutinesを使用して、2つのスレッドでの経過時間を計測しています。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val startTime = kotlin.system.nanoTime()

    val job1 = launch {
        // スレッド1の処理
        for (i in 1..500000) {
            print("")
        }
    }

    val job2 = launch {
        // スレッド2の処理
        for (i in 1..500000) {
            print("")
        }
    }

    job1.join()
    job2.join()

    val endTime = kotlin.system.nanoTime()
    val elapsedTime = (endTime - startTime) / 1_000_000.0
    println("マルチスレッド環境での処理にかかった時間: $elapsedTime ミリ秒")
}

このコードでは、launchを使用して2つのスレッドを並行して実行しています。

そして、それぞれのスレッドの終了をjoinメソッドで待機しています。

これにより、2つのスレッドが終了するまでの全体の経過時間を計測することができます。

○サンプルコード5:特定のタスクの実行時間を測定する

プログラミングにおいて、特定のタスクや関数の実行時間を正確に把握することは非常に重要です。

特にパフォーマンスチューニングやボトルネックの解消において、どの部分が時間がかかっているのかを知ることで、効果的な最適化が行えます。

Kotlinでは、これを簡単に行う方法が提供されています。

下記のサンプルコードでは、リスト内の数字の平均を求めるタスクの実行時間を測定しています。

fun calculateAverage(numbers: List<Int>): Double {
    return numbers.sum().toDouble() / numbers.size
}

fun main() {
    val numbers = List(1_000_000) { it + 1 }  // 1から1,000,000までの数字のリストを生成

    val startTime = kotlin.system.nanoTime()

    val average = calculateAverage(numbers)

    val endTime = kotlin.system.nanoTime()
    val elapsedTime = (endTime - startTime) / 1_000_000.0
    println("特定のタスク(平均の計算)の実行時間: $elapsedTime ミリ秒")
}

上記のコードでは、calculateAverage関数を定義し、1から1,000,000までの数字の平均を計算しています。

そして、nanoTime関数を使用して、このタスクの開始時と終了時の時間を取得し、その差を計算することで、タスクの実行にかかった時間をミリ秒単位で得ることができます。

このような方法で、Kotlinにおいて様々なタスクの実行時間を簡単に測定することができます。

これにより、ソフトウェアの最適化やデバッグ時に非常に役立つ情報を得ることができます。

○サンプルコード6:時間のフォーマットをカスタマイズして表示する

経過時間の測定結果をそのまま表示するのではなく、より分かりやすいフォーマットでユーザーに提示したい場合があります。

例えば、ミリ秒単位ではなく、秒や分で表示するなどのカスタマイズが考えられます。

Kotlinでこのようなフォーマットのカスタマイズを行う方法を紹介します。

下記のサンプルコードでは、経過時間を「分:秒.ミリ秒」の形式で表示しています。

fun formatElapsedTime(elapsedTimeMillis: Long): String {
    val minutes = elapsedTimeMillis / 60_000
    val seconds = (elapsedTimeMillis % 60_000) / 1_000
    val millis = elapsedTimeMillis % 1_000
    return String.format("%02d:%02d.%03d", minutes, seconds, millis)
}

fun main() {
    val startTime = kotlin.system.nanoTime()

    // 何らかの処理(ここでは例としてスリープを使用)
    Thread.sleep(7500)

    val endTime = kotlin.system.nanoTime()
    val elapsedTime = (endTime - startTime) / 1_000_000
    println("カスタマイズされたフォーマットでの経過時間: ${formatElapsedTime(elapsedTime)}")
}

このコードで、formatElapsedTime関数を使用して、ミリ秒単位の経過時間を「分:秒.ミリ秒」の形式に変換しています。

そして、処理の終了後に、カスタマイズされたフォーマットで経過時間を表示しています。

このように、Kotlinでは時間のフォーマットを簡単にカスタマイズすることができ、結果をユーザーフレンドリーな形で提示することができます。

●経過時間の測定の応用例

経過時間の測定は、単純に時間を計るだけでなく、多くの場面で有用なツールとなります。

特定の処理のパフォーマンスチューニングやデバッグ、ユーザーエクスペリエンスの向上のために、経過時間のデータを活用することが考えられます。

ここでは、経過時間の測定を応用したいくつかの例を、実際のKotlinのサンプルコードと共にご紹介します。

○サンプルコード7:経過時間をログに記録する

システムの動作を監視する際や、後から問題の原因を特定する際に、経過時間をログとして記録しておくことが有効です。

import java.io.File

fun logElapsedTime(taskName: String, elapsedTimeMillis: Long) {
    val logMessage = "$taskName の実行時間: $elapsedTimeMillis ミリ秒"
    File("log.txt").appendText("$logMessage\n")
    println(logMessage)
}

fun someTask(): Long {
    val startTime = System.currentTimeMillis()
    // ここに何らかの処理を記述
    Thread.sleep(1000)
    return System.currentTimeMillis() - startTime
}

fun main() {
    val elapsedTime = someTask()
    logElapsedTime("someTask", elapsedTime)
}

このコードを実行すると、someTaskという仮定のタスクが実行され、その経過時間がログファイル(log.txt)に記録されます。

このようにして、後からログを参照してタスクの実行時間を確認することができます。

○サンプルコード8:経過時間をユーザーに通知する

アプリケーションの動作において、特定のタスクが完了した際に、その経過時間をユーザーに知らせることで、ユーザーエクスペリエンスを向上させることができます。

fun notifyUser(taskName: String, elapsedTimeMillis: Long) {
    val message = "$taskName が完了しました。所要時間は $elapsedTimeMillis ミリ秒です。"
    println(message) // ここではコンソールに表示する例としています。
}

fun someUserTask(): Long {
    val startTime = System.currentTimeMillis()
    // 何らかのユーザー向けの処理
    Thread.sleep(2000)
    return System.currentTimeMillis() - startTime
}

fun main() {
    val elapsedTime = someUserTask()
    notifyUser("ユーザータスク", elapsedTime)
}

上記のサンプルコードでは、someUserTaskという仮定のユーザータスクを実行した後、その経過時間をユーザーに通知する形となっています。

このようにユーザーにタスクの完了を伝えることで、ユーザーの不安や疑問を解消する手助けとなります。

○サンプルコード9:経過時間に基づいて処理を分岐する

経過時間に基づいて処理を分岐させるという考え方は、特にパフォーマンスに敏感なアプリケーションやシステムにおいて重要となります。

例えば、ある処理が予想以上に時間を要してしまった場合、代替の処理に切り替える、またはユーザーに対して何らかのフィードバックを行うことが求められる場面が考えられます。

fun timeBasedTask(): Long {
    val startTime = System.currentTimeMillis()
    // 何らかのタスクを実行
    Thread.sleep((Math.random() * 2000).toLong())  // 0から2000ミリ秒の間でランダムにスリープ
    return System.currentTimeMillis() - startTime
}

fun main() {
    val elapsedTime = timeBasedTask()

    // 経過時間に応じた処理の分岐
    when {
        elapsedTime < 500 -> println("タスクは高速に完了しました。")
        elapsedTime in 500..1500 -> println("タスクは標準的な速度で完了しました。")
        else -> println("タスクが遅延しました。")
    }
}

このコードでは、timeBasedTaskという関数がランダムな時間でスリープすることで、擬似的に異なる処理時間を再現しています。

その後、main関数内で経過時間を確認し、その時間に応じて異なるメッセージを出力しています。

このサンプルコードを実行すると、経過時間が500ミリ秒未満の場合、”タスクは高速に完了しました。”というメッセージが出力されます。

一方、経過時間が500ミリ秒以上、1500ミリ秒以下の場合は、”タスクは標準的な速度で完了しました。”というメッセージが、それ以上の場合は”タスクが遅延しました。”というメッセージが出力されることがわかります。

○サンプルコード10:複数の経過時間を比較する

複数のタスクや処理を並列・並行に実行する際、それぞれの経過時間を比較して、最も効率的なものを選択するといった場面が考えられます。

それでは、Kotlinで複数のタスクの経過時間を比較し、最も短い時間で終了したタスクを特定するサンプルコードを紹介します。

fun taskA(): Long {
    val startTime = System.currentTimeMillis()
    Thread.sleep(1000)
    return System.currentTimeMillis() - startTime
}

fun taskB(): Long {
    val startTime = System.currentTimeMillis()
    Thread.sleep(1500)
    return System.currentTimeMillis() - startTime
}

fun taskC(): Long {
    val startTime = System.currentTimeMillis()
    Thread.sleep(800)
    return System.currentTimeMillis() - startTime
}

fun main() {
    val timeA = taskA()
    val timeB = taskB()
    val timeC = taskC()

    val fastestTask = when (minOf(timeA, timeB, timeC)) {
        timeA -> "タスクA"
        timeB -> "タスクB"
        else -> "タスクC"
    }

    println("$fastestTask が最も早く完了しました。")
}

上記のコードを実行すると、三つのタスク(taskA, taskB, taskC)のうち、taskCが最も早く800ミリ秒で完了するため、”タスクCが最も早く完了しました。”というメッセージが出力されます。

○サンプルコード11:測定の精度を向上させる方法

経過時間の測定において、精度は極めて重要です。

特に短時間の処理を計測する際や、非常に微細な時間の差を検出したい場合には、精度を最大限に高める方法を探求する必要があります。

Kotlinでは、System.nanoTime()を使用することで、ミリ秒単位よりも細かいナノ秒単位での時間計測が可能です。

下記のサンプルコードでは、System.nanoTime()を使用して経過時間をナノ秒単位で計測し、その精度の向上を表しています。

fun preciseTimeTask(): Long {
    val startNanoTime = System.nanoTime()
    // 何らかのタスクを実行
    Thread.sleep(10)  // 10ミリ秒スリープ
    return System.nanoTime() - startNanoTime
}

fun main() {
    val elapsedTimeInNano = preciseTimeTask()

    // 経過時間をナノ秒とミリ秒で表示
    println("タスクの経過時間(ナノ秒): $elapsedTimeInNano")
    println("タスクの経過時間(ミリ秒): ${elapsedTimeInNano / 1_000_000}")
}

このコードでは、preciseTimeTask関数を用いて、ナノ秒単位での経過時間を取得しています。

その後、main関数内で、ナノ秒およびミリ秒での経過時間を表示しています。

上記のコードを実行すると、経過時間がナノ秒単位とミリ秒単位の2つの形式で表示されることが確認できます。

ナノ秒単位の経過時間は非常に大きな数値として表示されるのに対し、ミリ秒単位での経過時間はその数値を1,000,000で割った結果が表示されます。

○サンプルコード12:ライブラリを活用した経過時間の高度な計測

Kotlinの標準ライブラリだけでなく、外部ライブラリを活用することで、さらに高度な経過時間の計測や分析が可能です。

特に、大規模なプロジェクトや複雑なシステムにおいては、専用のライブラリを使用して計測を行うことが推奨されます。

例として、kotlinx-benchmarkというライブラリを使用して、経過時間のベンチマークを取得する方法を解説します。

// このコードは簡易的なイメージを表すためのもので、正確な導入方法や使用方法は公式ドキュメントを参照してください。

@Benchmark
fun benchmarkSample() {
    // 何らかの処理を記述
    val list = List(1000) { it }
    list.sorted()
}

fun main() {
    val result = benchmarkSample()
    println("ベンチマークの結果: $result")
}

このコードでは、@Benchmarkというアノテーションを使用して、特定の処理のベンチマークを取得することができます。

実際には、ライブラリの導入やセットアップが必要となりますが、このようにライブラリを使用することで、標準ライブラリだけでは実現しづらい高度な計測や分析を行うことが可能となります。

●注意点と対処法

Kotlinでの経過時間の測定には多くの方法がありますが、それぞれの方法にはそれなりの注意点が存在します。

正確な時間計測を行うためには、これらの注意点を理解し、適切な対処法を取ることが求められます。

○経過時間の測定における誤差とその対処法

どのプログラミング言語を使用しても、完璧な時間測定は難しいものです。

コンピュータの内部処理、OSの動作、外部要因など、様々な要素が経過時間に影響を与える可能性があります。

Kotlinでの時間測定も例外ではありません。

下記のサンプルコードは、非常に短い時間を測定する例です。

fun measureShortTask(): Long {
    val startTime = System.nanoTime()
    // 非常に短いタスク
    val value = 1 + 1
    return System.nanoTime() - startTime
}

fun main() {
    val elapsedTime = measureShortTask()
    println("非常に短いタスクの経過時間: $elapsedTime ナノ秒")
}

このコードでは、加算処理を行って経過時間を計測しています。

しかし、このような短い処理を何度も繰り返して計測すると、異なる結果が得られることがあります。

このような微小な差は、JVMの最適化やガベージコレクション、他のアプリケーションの影響など、多くの要因に起因する可能性があります。

対処法としては、短いタスクを繰り返し実行し、平均値や中央値を取る方法が考えられます。

また、外部の影響を最小限に抑えるために、特定のタスクだけを隔離して実行する環境を構築することも効果的です。

○Kotlinでの時間測定の際のパフォーマンス問題への対応

経過時間の測定は、性能解析の一環としても使用されます。

しかし、度重なる時間測定は、パフォーマンスに影響を与える可能性があります。

例えば、ループ内で多数の時間測定を行うと、その測定自体がオーバーヘッドとなり、正確な結果を得ることが困難になることがあります。

対処法としては、測定する範囲を適切に選択することが求められます。

必要最低限の部分だけを計測し、測定回数を減らすことで、オーバーヘッドを軽減することができます。

また、測定用のコードと実際の運用コードを分離し、実運用時には測定コードを除外することも効果的です。

●カスタマイズ方法

Kotlinで経過時間を測定する際、出力結果を自分のニーズに合わせてカスタマイズすることが多々求められます。

測定結果をわかりやすく表示するためのフォーマット変更や、特定の情報だけをハイライトするなど、様々なカスタマイズが可能です。

ここでは、Kotlinでの時間測定結果のカスタマイズ方法についていくつかのテクニックをご紹介します。

○Kotlinでの時間測定結果のカスタマイズテクニック

  1. ミリ秒から秒や分に変換する

経過時間の測定結果がミリ秒単位で得られた場合、それを秒や分に変換することで、より直感的に結果を理解することができます。

下記のサンプルコードは、ミリ秒単位の結果を秒と分に変換する方法を表しています。

// ミリ秒を秒に変換する関数
fun convertMillisToSeconds(milliseconds: Long): Double {
    return milliseconds / 1000.0
}

// ミリ秒を分に変換する関数
fun convertMillisToMinutes(milliseconds: Long): Double {
    return convertMillisToSeconds(milliseconds) / 60.0
}

fun main() {
    val elapsedTimeMillis = 65000  // 例として65,000ミリ秒

    println("経過時間: ${convertMillisToSeconds(elapsedTimeMillis)} 秒")
    println("経過時間: ${convertMillisToMinutes(elapsedTimeMillis)} 分")
}

このコードを実行すると、経過時間がそれぞれ秒と分で表示されます。

具体的には、「経過時間: 65.0 秒」と「経過時間: 1.0833333333333333 分」という結果が得られることになります。

  1. 測定結果を色付きで表示する

経過時間が特定の閾値を超えた場合など、特定の条件に合致する測定結果を色付きで表示することで、目立たせることができます。

例えば、KotlinでANSIエスケープコードを利用して、色付きの文字列を表示するサンプルコードを見てみましょう。

fun printWithColor(message: String, elapsedTime: Long) {
    val ANSI_RED = "\u001B[31m"
    val ANSI_RESET = "\u001B[0m"

    if (elapsedTime > 1000) {
        println("$ANSI_RED$message$ANSI_RESET")
    } else {
        println(message)
    }
}

fun main() {
    val elapsedTime = 1500  // 例として1,500ミリ秒
    printWithColor("経過時間: $elapsedTime ミリ秒", elapsedTime)
}

このコードを実行すると、「経過時間: 1500 ミリ秒」というメッセージが赤色で表示されます。

これにより、特定の条件を満たす結果を一目で確認することが容易になります。

まとめ

Kotlinを利用して経過時間を効率的に測定する技術は、プログラミングの様々な場面で非常に有用です。

この記事では、経過時間の測定の基本から、具体的なサンプルコードを交えての方法、応用例、そしてカスタマイズ方法までを解説しました。

Kotlinでの経過時間測定の技術を手に入れた今、あなたの次のステップは、これを実際の開発現場で活かしていくことです。

是非とも、この知識を最大限に生かして、高品質なアプリケーションやシステムの開発に挑戦してみてください。