Javaで経過時間を測定する
1. 概要
この記事では、Javaで経過時間を測定する方法を見ていきます。 While this may sound easy, there’re a few pitfalls that we must be aware of.
経過時間を測定する機能を提供する標準のJavaクラスと外部パッケージについて説明します。
2. 簡単な測定
2.1. currentTimeMillis()
Javaで経過時間を測定する必要がある場合、次のようにしようとする場合があります。
long start = System.currentTimeMillis();
// ...
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;
コードを見ると、まったく理にかなっています。 開始時にタイムスタンプを取得し、コードが終了すると別のタイムスタンプを取得します。 経過時間は、これら2つの値の差です。
ただし、the result may and will be inaccurate as System.currentTimeMillis() measures wall-clock time.の実時間は、さまざまな理由で変更される可能性があります。 システム時間を変更すると、結果に影響する可能性があります。そうしないと、うるう秒によって結果が混乱します。
2.2. nanoTime()
java.lang.Systemクラスの別のメソッドはnanoTime()です。 Java documentationを見ると、次のステートメントが見つかります。
「この方法は経過時間を測定するためにのみ使用でき、システムや実時間のその他の概念とは関係ありません。」
それを使用しましょう:
long start = System.nanoTime();
// ...
long finish = System.nanoTime();
long timeElapsed = finish - start;
コードは基本的に以前と同じです。 唯一の違いは、タイムスタンプの取得に使用される方法–currentTimeMillis()ではなくnanoTime()です。
また、nanoTime()は、明らかにナノ秒単位で時間を返すことにも注意してください。 したがって、経過時間が異なる時間単位で測定される場合、それに応じて変換する必要があります。
たとえば、ミリ秒に変換するには、結果をナノ秒で1.000.000で除算する必要があります。
nanoTime()のもう1つの落とし穴は、even though it provides nanosecond precision, it doesn’t guarantee nanosecond resolution(つまり、 値が更新される頻度)。
ただし、解像度が少なくともcurrentTimeMillis()の解像度と同じになることは保証されます。
3. Java 8
Java 8を使用している場合は、新しいjava.time.Instantクラスとjava.time.Durationクラスを試すことができます。 どちらも不変でスレッドセーフであり、新しいjava.time API内のすべてのクラスと同様に、独自のタイムスケールであるJava Time-Scale,を使用します。
3.1. Javaタイムスケール
時間を測定する従来の方法は、1日を60秒の60分である24時間に分割することで、1日86.400秒になります。 ただし、太陽の日は常に等しいとは限りません。
UTCタイムスケールでは、実際には1日で86.399または86.401 SI秒を使用できます。 SI秒は科学的な「標準国際秒」であり、セシウム133原子の放射周期によって定義されます)。 これは、日を太陽に合わせるために必要です。
The Java Time-Scale divides each calendar day into exactly 86.400 subdivisions, known as seconds。 うるう秒はありません。
3.2. Instantクラス
Instantクラスは、タイムライン上の瞬間を表します。 基本的に、これは1970-01-01T00:00:00Zの標準Javaエポック以降の数値のタイムスタンプです。
現在のタイムスタンプを取得するために、Instant.now()静的メソッドを使用できます。 このメソッドでは、オプションのClockパラメータを渡すことができます。 省略すると、デフォルトのタイムゾーンのシステムクロックが使用されます。
We can store start and finish times in two variables, as in previous examples.次に、両方の瞬間の間の経過時間を計算できます。
さらに、Durationクラスとそのbetween()メソッドを使用して、2つのInstantオブジェクト間の期間を取得できます。 最後に、Durationをミリ秒に変換する必要があります。
Instant start = Instant.now();
// CODE HERE
Instant finish = Instant.now();
long timeElapsed = Duration.between(start, finish).toMillis();
4. StopWatch
ライブラリに移ると、Apache Commons Langは、経過時間を測定するために使用できるStopWatchクラスを提供します。
4.1. メーベン依存
pom.xmlを更新することにより、最新バージョンを取得できます。
org.apache.commons
commons-lang3
3.7
依存関係の最新バージョンは、hereで確認できます。
4.2. StopWatchによる経過時間の測定
まず、クラスのインスタンスを取得する必要があり、次に経過時間を簡単に測定できます。
StopWatch watch = new StopWatch();
watch.start();
ウォッチを実行したら、ベンチマークするコードを実行し、最後にstop()メソッドを呼び出すだけです。 最後に、実際の結果を取得するために、getTime()を呼び出します。
watch.stop();
System.out.println("Time Elapsed: " + watch.getTime()); // Prints: Time Elapsed: 2501
StopWatch has a few additional helper methods that we can use in order to pause or resume our measurement.これは、ベンチマークをより複雑にする必要がある場合に役立つことがあります。
最後に、クラスはスレッドセーフではないことに注意してください。
5. 結論
Javaで時間を測定する方法はたくさんあります。 currentTimeMillis()を使用して、非常に「従来の」(そして不正確な)方法について説明しました。 さらに、Apache CommonのStopWatchをチェックし、Java8で利用可能な新しいクラスを調べました。
全体として、経過時間を簡単かつ正確に測定するには、nanoTime()メソッドで十分です。 また、currentTimeMillis()よりも入力が短くなります。
ただし、適切なベンチマークを行うために、時間を手動で測定する代わりに、Java Microbenchmark Harness(JMH)などのフレームワークを使用できることに注意してください。 このトピックはこの記事の範囲を超えていますが、it hereを調査しました。
最後に、いつものように、ディスカッション中に使用されたコードはover on GitHubで見つけることができます。