時間の測定
Kotlin標準ライブラリには、さまざまな単位で時間を計算し測定するためのツールが用意されています。 正確な時間測定は、以下のようなアクティビティにとって重要です。
- スレッドやプロセスの管理
- 統計情報の収集
- タイムアウトの検出
- デバッグ
デフォルトでは、時間は単調増加するタイムソース(monotonic time source)を使用して測定されますが、他のタイムソースも設定可能です。 詳細は「タイムソースの作成」を参照してください。
期間の計算
時間の量を表現するために、標準ライブラリにはDuration
クラスがあります。Duration
は、DurationUnit
enumクラスの以下の単位で表現できます。
NANOSECONDS
MICROSECONDS
MILLISECONDS
SECONDS
MINUTES
HOURS
DAYS
Duration
は、正、負、ゼロ、正の無限大、負の無限大のいずれかになります。
期間の作成
Duration
を作成するには、Int
、Long
、Double
型で利用可能な拡張プロパティであるnanoseconds
、microseconds
、milliseconds
、seconds
、minutes
、hours
、days
を使用します。
日(Days)は24時間の期間を指します。カレンダー上の日付ではありません。
例:
import kotlin.time.*
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.days
fun main() {
val fiveHundredMilliseconds: Duration = 500.milliseconds
val zeroSeconds: Duration = 0.seconds
val tenMinutes: Duration = 10.minutes
val negativeNanosecond: Duration = (-1).nanoseconds
val infiniteDays: Duration = Double.POSITIVE_INFINITY.days
val negativeInfiniteDays: Duration = Double.NEGATIVE_INFINITY.days
println(fiveHundredMilliseconds) // 500ms
println(zeroSeconds) // 0s
println(tenMinutes) // 10m
println(negativeNanosecond) // -1ns
println(infiniteDays) // Infinity
println(negativeInfiniteDays) // -Infinity
}
Duration
オブジェクトを使って基本的な算術演算を行うこともできます。
import kotlin.time.*
import kotlin.time.Duration.Companion.seconds
fun main() {
val fiveSeconds: Duration = 5.seconds
val thirtySeconds: Duration = 30.seconds
println(fiveSeconds + thirtySeconds)
// 35s
println(thirtySeconds - fiveSeconds)
// 25s
println(fiveSeconds * 2)
// 10s
println(thirtySeconds / 2)
// 15s
println(thirtySeconds / fiveSeconds)
// 6.0
println(-thirtySeconds)
// -30s
println((-thirtySeconds).absoluteValue)
// 30s
}
文字列表現の取得
Duration
の文字列表現を持つことは、それを印刷、シリアライズ、転送、または保存できるため、有用です。
文字列表現を取得するには、.toString()
関数を使用します。デフォルトでは、時間は存在する各単位を使用して報告されます。例: 1h 0m 45.677s
または -(6d 5h 5m 28.284s)
出力を設定するには、.toString()
関数で目的のDurationUnit
と小数点以下の桁数を関数パラメータとして使用します。
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
fun main() {
// Print in seconds with 2 decimal places
println(5887.milliseconds.toString(DurationUnit.SECONDS, 2))
// 5.89s
}
ISO-8601互換の文字列を取得するには、toIsoString()
関数を使用します。
import kotlin.time.Duration.Companion.seconds
fun main() {
println(86420.seconds.toIsoString()) // PT24H0M20S
}
期間の変換
Duration
を異なるDurationUnit
に変換するには、以下のプロパティを使用します。
inWholeNanoseconds
inWholeMicroseconds
inWholeSeconds
inWholeMinutes
inWholeHours
inWholeDays
例:
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
fun main() {
val thirtyMinutes: Duration = 30.minutes
println(thirtyMinutes.inWholeSeconds)
// 1800
}
あるいは、目的のDurationUnit
を以下の拡張関数の関数パラメータとして使用することもできます。
.toInt()
.toDouble()
.toLong()
例:
import kotlin.time.Duration.Companion.seconds
import kotlin.time.DurationUnit
fun main() {
println(270.seconds.toDouble(DurationUnit.MINUTES))
// 4.5
}
期間の比較
Duration
オブジェクトが等しいかどうかを確認するには、等価演算子(==
)を使用します。
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
fun main() {
val thirtyMinutes: Duration = 30.minutes
val halfHour: Duration = 0.5.hours
println(thirtyMinutes == halfHour)
// true
}
Duration
オブジェクトを比較するには、比較演算子(<
、>
)を使用します。
import kotlin.time.Duration.Companion.microseconds
import kotlin.time.Duration.Companion.nanoseconds
fun main() {
println(3000.microseconds < 25000.nanoseconds)
// false
}
期間をコンポーネントに分解する
Duration
をその時間コンポーネントに分解し、さらにアクションを実行するには、toComponents()
関数のオーバーロードを使用します。目的のアクションを関数またはラムダ式として関数パラメータに追加します。
例:
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
fun main() {
val thirtyMinutes: Duration = 30.minutes
println(thirtyMinutes.toComponents { hours, minutes, _, _ -> "${hours}h:${minutes}m" })
// 0h:30m
}
この例では、ラムダ式は、未使用のseconds
とnanoseconds
パラメータに対してアンダースコア(_
)を使用して、hours
とminutes
を関数パラメータとして持ちます。この式は、文字列テンプレートを使用して連結された文字列を返し、hours
とminutes
の目的の出力形式を取得します。
時間の測定
時間の経過を追跡するために、標準ライブラリは以下のことを簡単に行えるツールを提供します。
- 目的のタイムユニットでコードの実行にかかる時間を測定します。
- ある時点をマークします。
- 2つの時点を比較および減算します。
- 特定の時点からどれくらいの時間が経過したかを確認します。
- 現在の時間が特定の時点を過ぎたかどうかを確認します。
コード実行時間の測定
コードブロックの実行にかかる時間を測定するには、measureTime
インライン関数を使用します。
import kotlin.time.measureTime
fun main() {
val timeTaken = measureTime {
Thread.sleep(100)
}
println(timeTaken) // e.g. 103 ms
}
コードブロックの実行にかかる時間を測定し、そのコードブロックの値を返すには、インライン関数measureTimedValue
を使用します。
例:
import kotlin.time.measureTimedValue
fun main() {
val (value, timeTaken) = measureTimedValue {
Thread.sleep(100)
42
}
println(value) // 42
println(timeTaken) // e.g. 103 ms
}
デフォルトでは、どちらの関数も単調増加するタイムソースを使用します。
時点をマークする
特定の時点をマークするには、TimeSource
インターフェースとmarkNow()
関数を使用してTimeMark
を作成します。
import kotlin.time.*
fun main() {
val timeSource = TimeSource.Monotonic
val mark = timeSource.markNow()
}
時間の差を測定する
同じタイムソースからのTimeMark
オブジェクト間の差を測定するには、減算演算子(-
)を使用します。
同じタイムソースからのTimeMark
オブジェクトを比較するには、比較演算子(<
、>
)を使用します。
例:
import kotlin.time.*
fun main() {
val timeSource = TimeSource.Monotonic
val mark1 = timeSource.markNow()
Thread.sleep(500) // Sleep 0.5 seconds.
val mark2 = timeSource.markNow()
repeat(4) { n ->
val mark3 = timeSource.markNow()
val elapsed1 = mark3 - mark1
val elapsed2 = mark3 - mark2
println("Measurement 1.${n + 1}: elapsed1=$elapsed1, elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
}
println(mark2 > mark1) // This is true, as mark2 was captured later than mark1.
// true
}
期限が過ぎたか、タイムアウトに達したかを確認するには、hasPassedNow()
とhasNotPassedNow()
拡張関数を使用します。
import kotlin.time.*
import kotlin.time.Duration.Companion.seconds
fun main() {
val timeSource = TimeSource.Monotonic
val mark1 = timeSource.markNow()
val fiveSeconds: Duration = 5.seconds
val mark2 = mark1 + fiveSeconds
// It hasn't been 5 seconds yet
println(mark2.hasPassedNow())
// false
// Wait six seconds
Thread.sleep(6000)
println(mark2.hasPassedNow())
// true
}
タイムソース
デフォルトでは、時間は単調増加するタイムソースを使用して測定されます。単調増加するタイムソースは、常に前方へ進み、タイムゾーンのような変動の影響を受けません。単調増加する時間(monotonic time)の代替として、経過実時間(elapsed real time)があり、これはウォールクロック時間(wall-clock time)とも呼ばれます。経過実時間は、別の時点を基準として測定されます。
プラットフォームごとのデフォルトタイムソース
この表は、各プラットフォームにおける単調増加する時間のデフォルトソースについて説明しています。
プラットフォーム | ソース |
---|---|
Kotlin/JVM | System.nanoTime() |
Kotlin/JS (Node.js) | process.hrtime() |
Kotlin/JS (browser) | window.performance.now() or Date.now() |
Kotlin/Native | std::chrono::high_resolution_clock or std::chrono::steady_clock |
タイムソースの作成
異なるタイムソースを使用したい場合もあるでしょう。例えばAndroidでは、System.nanoTime()
はデバイスがアクティブな間のみ時間をカウントします。デバイスがディープスリープに入ると、時間の追跡ができなくなります。デバイスがディープスリープ中でも時間を追跡し続けるには、SystemClock.elapsedRealtimeNanos()
を使用するタイムソースを作成できます。
object RealtimeMonotonicTimeSource : AbstractLongTimeSource(DurationUnit.NANOSECONDS) {
override fun read(): Long = SystemClock.elapsedRealtimeNanos()
}
次に、そのタイムソースを使用して時間測定を行うことができます。
fun main() {
val elapsed: Duration = RealtimeMonotonicTimeSource.measureTime {
Thread.sleep(100)
}
println(elapsed) // e.g. 103 ms
}
kotlin.time
パッケージの詳細については、標準ライブラリAPIリファレンスを参照してください。