検索

キーワード


【Java】処理時間/経過時間計測(ミリ秒)に使える2つのメソッドを解説

  • 公開日:2021-01-26 08:21:20
  • 最終更新日:2021-01-25 19:01:20
【Java】処理時間/経過時間計測(ミリ秒)に使える2つのメソッドを解説

こんにちは、研修を終えた駆け出しエンジニアの伊藤です。
東京ITカレッジのJava研修で学んだ内容を復習も兼ねて記事にしたいと思います。
今回は、プログラムの処理時間(経過時間)の計測方法を解説していきます。
この記事では、処理時間計測の具体的なサンプルプログラムを通して基本的なメソッドを紹介します。
Javaやプログラムについて勉強し始めた方の参考になれば幸いです!
(eclipseを使用して計算を行っています)


関連記事:よく使う日時クラス解説まとめ

     日時取得の Date/Calendar クラスの基本と使い方を解説

     Java8からの現在や指定日時取得 9クラスの基本と使い方を解説




処理時間(経過時間)を計測する理由・方法

処理時間を計測することで、プログラムの実行にかかる時間を調べたり、似たようなメソッドではどちらの処理速度が速いかを比較したりすることで、プログラムの改善や処理の効率化を図ることができます。

Javaの処理時間を計測するためのメソッドは2つあります。

・System.currentTimeMillisメソッド(ミリ秒)
・System.nanoTimeメソッド(ナノ秒)

実際は、処理を開始した時刻(開始タイムスタンプ)と終了した時刻(終了タイムスタンプ)を取得し、その差分で計測ができます。(プログラムの速度は、動作環境(OS、CPU、メモリ、HDD等)・データ構造・アルゴリズムなどによって変わります。)





ミリ秒単位で処理時間を計測する方法

java.lang.Systemクラスの「System.currentTimeMillisメソッド」を使用することでミリ秒単位の処理時間を計測することができます。

 long 変数名 = System.currentTimeMillis();

これで、現在時刻の取得ができます。

currentTimeMillisメソッドは、現在の時刻をミリ秒の単位で返しますが、精度はOSに依存するため、正確さにかける点があります。

(例:OSが時間を計測する単位が10ミリ秒の場合、返る値の単位はミリ秒になります。しかし、制度はミリ秒ではなく10ミリ秒となります)

なお、currentTimeMillisメソッド実行時にパラメータは不要です。

取得結果のミリ秒は協定世界時1970年1月1日午前0時0分0秒からの経過時間(エポック秒=UNIX時間から経過した時間)となります。


関連記事:java.lang.Systemクラス(JavaDoc‐currentTimeMillisメソッド)



UNIX時間とは

UNIX時間とはコンピュータシステム上での時刻表現の一つです。

協定世界時(UTC)での1970年1月1日午前0時0分0秒から形式的な経過秒数(閏秒を計算したもの)として表すものを、UNIX時間=エポック秒といいます。
(例:1970年1月1日午前0時0分1秒であれば、エポック秒は 1 を返します。0時1分0秒では、60 を返します)

時刻表現としてその他に世界時(地球の自転に基づく時刻系)があります。


UTCとの使い分けは、

UT1(世界時)  :天測航法及び測量における暦の独立引数
UTC(協定世界時):法令・通信・民生用等

となっています。


UTCは、国際原子時(TAI)に由来し原子時計によって時刻が決まり、UT1は地球の自転によって時刻が決まります。

地球の自転は一定ではないため、UT1とUTCにはずれが生じてしまいます。

その為、UTCはUT1に同調するべく調整された基準時刻となっています。UT1との差を調整するために追加もしくは削除される秒のことを「閏秒」と言います。2019年までに27回閏秒の追加調整(すべて1秒追加)がありました。

この閏秒での調整が入ることにより、処理時間の計測値が違ってくる場合がありるので、正確な処理時間を求める場合には注意が必要です。

また、世界各地の標準時(同じ区域ごとに区切った時間帯(時差・UTCからの時間差)、タイムゾーン)は協定世界時を基準としています。日本標準時(JST)は協定世界時より9時間進めた時間(UTC+09:00)となっています。



それでは、現在時刻(現在のエポック秒経過時間)取得プログラムを見てみましょう。

//現在時刻(現在のエポック秒経過時間)取得サンプルプログラム
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class NowTimeStampSample {

	public static void main(String[] args) {
		//現在日時の取得
		LocalDateTime ldt = LocalDateTime.now();
		//現在日時をlong型に変換
		ZonedDateTime zdt = ldt.atZone(ZoneOffset.ofHours(+9));
		long epochMilli = zdt.toInstant().toEpochMilli();
		//現在時刻(現在のエポック秒経過時間)取得
		long nowTime = System.currentTimeMillis();
		System.out.println("---取得結果--------------------");
		System.out.println("現在日時 : " + ldt);
		System.out.println("(long型  : " + epochMilli + ")");
		System.out.println("現在時刻 : " + nowTime + "ミリ秒");
	}
}

---取得結果--------------------
現在日時 : 2020-08-31T16:33:23.096435300
(long型  : 1598859203096)
現在時刻 : 1598859203097ミリ秒

これで、現在時刻のミリ秒取得ができました。

今回のサンプルプログラムでは本当にcurrentTimeMillis()メソッドのlong型が現在時刻なのかを確認するため、LocalDateTimeで現在日時を取得し、それをlong型に変換しています。

ZonedDateTimeとtoInstant・toEpochMilliメソッドを使ってLocalDateTimeで取得した現在日時をlong型のミリ秒に変換しています。

取得結果から、現在日時のlong型とcurrentTimeMillis()メソッドで取得した現在時刻がほぼ同じ(1ミリ秒の差はシステムの処理順によるもの)、同時刻ということが分かります。

ここから、差分を求め処理時間の計測結果を取得してみましょう。


System.currentTimeMillisメソッドで開始タイムスタンプと終了タイムスタンプを取得します。

その後、開始終了の差分を求めることで、処理にかかった時間が算出されます。

現在時刻取得サンプルプログラムの結果から分かるように、返る値はint型の最大値を超えるため、戻り値はlong型となり、その値はエポック秒から経過した時間を返します。


System.currentTimeMillisメソッドを使って、ミリ秒単位の処理時間を計測する方法を実際のプログラムで見てみましょう。

//currentTimeMillisメソッドを使用した処理時間取得サンプルプログラム
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class CurrentTimeMillisSample {

	public static void main(String[] args) {
		//開始タイムスタンプ(処理前の時刻)を取得
		long startTime = System.currentTimeMillis();
		//
		//時間計測をする処理内容
		//現在時刻取得サンプルプログラムの一部
		LocalDateTime ldt = LocalDateTime.now();
		ZonedDateTime zdt = ldt.atZone(ZoneOffset.ofHours(+9));
		long epochMilli = zdt.toInstant().toEpochMilli();
		System.out.println("現在時刻 : " + epochMilli);
		//
		//終了タイムスタンプ(処理後の時刻)を取得
		long endTime = System.currentTimeMillis();
		//処理結果
		System.out.println("---取得結果--------------------");
		System.out.println("開始時刻 : " + startTime + "ミリ秒");
		System.out.println("終了時刻 : " + endTime + "ミリ秒");
		System.out.println("処理時間 : " + (endTime - startTime) + "ミリ秒");
	}
}

現在時刻 : 1598863617258
---取得結果--------------------
開始時刻 : 1598863617251ミリ秒
終了時刻 : 1598863617259ミリ秒
処理時間 : 8ミリ秒

今回のプログラムの処理内容では、現在時刻(現在のエポック秒経過時間)取得サンプルプログラムの処理時間を計測してみました。

開始タイムスタンプを取得して、時間計測をする処理が終わったら終了タイムスタンプを取得します。その後、終了タイムスタンプから開始タイムスタンプを減算し、その結果が処理時間として表示されています。

今回の処理時間は8ミリ秒でした。このように、System.currentTimeMillisメソッド使用することで、ミリ秒単位の処理時間計測ができます。




ナノ秒単位で処理時間を計測する方法

「System.nanoTimeメソッド」を使用することで、ナノ秒単位で処理時間を計測することができます。

long 変数名1 = System.nanoTime();

これで、システム時間の取得ができます。

ナノ秒はミリ秒よりも精密に経過時間を計測することができるため、正確さを求めるのであればcurrentTimeMillisメソッドよりも、ナノ秒単位で計測できる「System.nanoTimeメソッド」を使う方が良いでしょう。

System.nanoTimeメソッドの戻り値もlong型ですが、返す時間はcurrentTimeMillisメソッドとは違い、プログラムを実行しているシステムの時間になります。

System.currentTimeMillisメソッドのエポック秒のように基準となる時間があるわけではないので、現在の時間を調べたり、取得したりすることはできません。

System.nanoTimeメソッド実行時にパラメータは不要です。

ナノ秒の取得方法をプログラムで見てみましょう。

//nanoTimeメソッドを使用した処理時間取得サンプルプログラム
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class NanoTimeSample {

	public static void main(String[] args) {
		//開始タイムスタンプを取得
		long startTime = System.nanoTime();
		//
		//時間計測をする処理
		//現在時刻取得サンプルプログラムの現在日時取得部分
		LocalDateTime ldt = LocalDateTime.now();
		ZonedDateTime zdt = ldt.atZone(ZoneOffset.ofHours(+9));
		long epochMilli = zdt.toInstant().toEpochMilli();
		System.out.println("現在時刻 : " + epochMilli);
		//
		//終了タイムスタンプを取得
		long endTime = System.nanoTime();
		long result = endTime - startTime;
		//処理結果
		System.out.println("---取得結果--------------------");
		System.out.println("開始時刻 : " + startTime + "ナノ秒");
		System.out.println("終了時刻 : " + endTime + "ナノ秒");
		System.out.println("処理時間 : " + result + "ナノ秒");
		System.out.println("処理時間 : " + (result / 1000) + "マイクロ秒");
		System.out.println("処理時間 : " + (result / 1000000) + "ミリ秒");
	}
}

---取得結果--------------------
開始時刻 : 333244515341200ナノ秒
終了時刻 : 333244523369200ナノ秒
処理時間 : 8028000ナノ秒
処理時間 : 8028マイクロ秒
処理時間 : 8ミリ秒

今回のプログラムは、ミリ秒を求めた時とほとんど同じ内容にしています。

変更点は、ナノ秒でのシステム時間の取得と結果を秒数の単位です。

処理時間を見ると、ミリ秒の時よりも細かい値になっています。

今回の処理時間を1000000で割ると、約8ミリ秒となりSystem.currentTimeMillisメソッドのサンプルプログラムとだいたい同じ結果となりました。

しかし、System.currentTimeMillisメソッドよりもSystem.nanoTimeメソッドの方がより正確な値となっていることが分かりますね。

単位では、

ミリ秒(1秒の1/1000秒)
マイクロ秒(1秒の1/1000000秒)
ナノ秒(1秒の1/1000000000秒)

と細かくなります。

今回のプログラムでは、ミリ秒とナノ秒の間のマイクロ秒も表示してあります。
どの単位で処理時間の計測が必要なのかを考え、プログラムを作っていくと良いでしょう。


関連記事:java.lang.Systemクラス(JavaDoc‐nanoTimeメソッド



メソッドの処理速度を比較する方法

ここでは、実際に2つのメソッドを使ってどちらの処理速度が速いかを検証してみます。

今回のプログラムでは、文字列検索に関する処理時間の計測を行ってみます。

indexOfメソッド(先頭から検索)と、lastIndexOfメソッド(後方から検索)の処理速度を比較してみましょう。


//処理速度比較サンプルプログラム(indexOfメソッド・lastIndexOfメソッド)
public class ComparisonSample {

	public static void main(String[] args) {
		//検索対象文字列
		String str1 = "akjdfaldnfadngadfhgjdfllaksjflajfjaweohoiuerjfkj1dflja16a1af2e4q65h4dfh123g1hqmz";
		String str2 ="oiu";
		//開始タイムスタンプを取得
		long startTime = System.nanoTime();
		//
		//時間計測をする処理(indexOf-先頭検索)
		int search1 = str1.indexOf(str2);	//先頭検索
		if (search1 != -1) {
			System.out.println("indexOf     : 先頭から " + search1 + "番目に文字列があります"); 	//39番目に見つかる(先頭は0番)
} else { System.out.println(str2 + "はありません"); } // //終了タイムスタンプを取得 long endTime = System.nanoTime(); long result = endTime - startTime; // //開始タイムスタンプを取得 long startTime2 = System.nanoTime(); // //時間計測をする処理(lastIndexOf-後方検索) int search2 = str1.lastIndexOf(str2); //後方検索 if (search2 != -1) { System.out.println("lastIndexOf : 先頭から " + search2 + "番目に文字列があります"); //39番目に見つかる(先頭は0番) } else { System.out.println(str2 + "はありません"); } // //終了タイムスタンプを取得 long endTime2 = System.nanoTime(); long result2 = endTime2 - startTime2; //処理結果 System.out.println("---取得結果--------------------"); System.out.println("先頭検索時間 : " + result + "ナノ秒"); System.out.println("後方検索時間 : " + result2 + "ナノ秒"); } }

indexOf     : 先頭から 39番目に文字列があります
lastIndexOf : 先頭から 39番目に文字列があります
---取得結果--------------------
先頭検索時間 : 480200ナノ秒
後方検索時間 :  47300ナノ秒

今回のプログラムでは、どちらのメソッドでも中央の文字列を探しましたが、取得結果からlastIndexOfメソッドの後方検索の方が処理時間が速いことが分かりました。

今回は80文字という短いものでの検索でしたが、約 440000ナノ秒の差が出ました。

ただし、今回のサンプル通りlastIndexOfメソッドがどのプログラムでも早くなるということではありません。


・文字列データや実行環境により結果は異なり、indexOfメソッドよりもlastIndexOfメソッドが高速であるという意味ではありません。

・このプログラムで実際に時間がかかっているのは、入出力処理(System.out.println)の部分であり、純粋な文字列検索のみの処理時間ではありません。


処理中のSystem.out.println(特に最初に呼び出した時)に時間がかかっています。System.out.printlnの部分をコメントアウトすると、

---取得結果--------------------
先頭検索時間 : 3700ナノ秒
後方検索時間 :  4800ナノ秒

に変わります。(これも、文字列データや実行環境によって異なってきます)

メソッドの処理速度の違いを知っていることで、より良いプログラムが書けるようになるでしょう。



日時の取得については、次の記事で詳しく解説しているので、興味のある方はぜひ参考にしてみてください。


Date/Calendar クラスの日時取得の方法を知りたい場合は、次の記事を参考にしてください。
【Java】日時取得の Date/Calendar クラスの基本と使い方を解説」   

Java8から導入されたDateAndTimeAPI(新API)からの日時取得の方法を知りたい場合は、次の記事を参考にしてください。
Java8からの現在や指定日時取得 9クラスの基本と使い方を解説」  



まとめ

今回はJavaプログラムの処理時間計測の方法(System.currentTimeMillisメソッド、System.nanoTimeメソッド)について紹介しました。

処理時間の計測では、タイムスタンプを取得して行います。また、ミリ秒、ナノ秒などがありますので、システムに合わせて使い分けると良いでしょう。

関連記事:よく使う日時クラス解説まとめ


【著者】

伊藤

Javaを研修で3か月学んだ、駆け出しのエンジニアです。
現在は、ベンダー資格を取得するため、勉強を日課にできるよう努力中です。

よく読まれている記事
【Java】JSPでタグライブラリを使う(JSTL)

【Java】JSPでタグライブラリを使う(JSTL)

こんにちは。エンジニアの新田です!ここでは、システムエンジニアとして働いている私が、システム開発手法や開発言語について紹介していこうと思います。今回は、JSPの標準タグライブラリ「JSTL」について紹介します。Javaについて勉強している方、Webアプリケーションを構築したいと思っている方の参考になれば幸いです!関連記事リンク: 【Java】JSPの基本的な構文/【Java】JSPのアクションタグ

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!そもそもStringとは何?『 String 』... Java言語において文字列のデータ型を指します。基本デ

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!Stringクラスformatメソッドの文字列整形【java.utilパッケージ】Formatterクラスfo

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者にも✅、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!文字列を扱う3つのクラス【java.langパッケージ】java.langパッケージの文字列を扱うクラスにはS

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!プリミティブ型と参照型プログラム開発では型を持った変数を使ってデータのやり取りをしますが、データ型によって仕様