Javaで経過時間を測定する

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で見つけることができます。