【Python】時間計測の方法をわかりやすく解説!timeモジュール・timeit・perf_counter・process_timeなど
はじめに
Pythonでプログラムの実行速度を測定することは、開発や学習の過程で非常に重要です。
たとえば、処理が遅いと感じる部分を特定して改修したり、外部サービスとの連携部分の動作を素早く評価したりできます。
しかし、いざ「実行時間を計測したい」と考えたときに、どの方法を使えばよいのか迷うことはないでしょうか。
実際、Pythonにはいくつかのアプローチがあります。
有名どころでは timeモジュール や timeitモジュール、そして perf_counter や process_time など、多様な方法が用意されています。
目的や用途によって選び方が異なるため、それぞれの特徴や使い分け方を理解しておくと便利です。
そこで本記事では、Python初心者の方でも理解できるよう、各方法を具体的なコード例と実務シーンに結びつけて解説します。
また、実務で陥りやすい注意点や、複数回の計測におけるポイントも押さえます。
「どうしてこんなに遅いんだろう」というときに役立つ知識を学んでいきましょう。
この記事を読むとわかること
- Pythonで実行時間を測定する主な方法と手順
- timeモジュールやtimeitモジュールを使った具体的なコード例
- perf_counterやprocess_timeといった、より正確な計測方法
- 開発や運用で役立つ実務での利用例
- 計測結果を解釈するときの注意点やエラートラブルへの対処法
Pythonで時間計測が求められるシーン
Pythonで時間計測が必要になるシーンはさまざまです。
大きく分けると、自作のスクリプトが遅く感じられるケースや、外部サービスと連携している部分のパフォーマンスを検証したいケースなどが考えられます。
また、業務システムでは一定の処理時間を満たすことが求められることもあるため、計測の重要性は高いでしょう。
さらに、学習中に「自分の書いたコードはどの程度効率的に動作しているのだろう」と疑問に思うことがあるかもしれません。
その際に正しい方法で測定を行えば、アルゴリズムの改善ポイントや処理のネックを可視化できます。
これによりコードの修正方針が立てやすくなり、結果として読みやすく、動作がスムーズなアプリケーションを作ることにつながります。
コードのパフォーマンス検証
たとえば、ある処理が想定以上に時間を要してしまい、ユーザー体験が悪化するリスクがある場合、時間計測は真っ先に行われます。
単純にコードを書きかえてみるだけでは、「どれくらい改善されたのか」を客観的に把握しづらいかもしれません。
計測を行えば、修正前後の数値を比較し、処理が本当に速くなったのかを確認できます。
初心者の方は「この書き方とあの書き方、どちらが速いんだろう」と疑問に思うことがよくあるでしょう。
そんなときに計測をする習慣がついていると、データに基づく判断がしやすくなります。
実際にコードの一部を変更してから計測ツールを回し、違いを比較するのは地味ながら役立つ手法です。
システム全体のチューニング
Pythonをバックエンドで使うWebアプリケーションや、データ分析のパイプラインなどでは、プログラムの処理時間が全体に大きく影響します。
ユーザー数が増えると負荷がかかりやすくなるため、限られたリソースの中でどの程度耐えられるかを把握する必要があります。
このとき、どの部分に時間がかかっているのか細かく測定しておけば、最適化ポイントが明確になります。
たとえばデータ分析の処理フローにおいて、前処理部分で異様に時間がかかっているといった問題が発見されるかもしれません。
そこからさらに原因を深掘りすると、大きなファイルを読み込む段階で無駄な操作を繰り返していることが判明する場合もあります。
こういったケースでも計測結果が具体的だと、改善策を講じるモチベーションにつながるでしょう。
Pythonで使える代表的な時間計測手法
Pythonには、標準ライブラリの中だけでも時間計測に役立つ機能が複数存在します。
代表的な手法には、以下のようなものがあります。
- timeモジュール: シンプルに現在の時刻を取得したいときに便利
- timeitモジュール: 特定のコード片を複数回実行し、平均実行時間を求めるときに使いやすい
- perf_counter, process_time: より正確な計測が必要なケースで活用しやすい
一見するとどれも似たようなことをしているように感じられますが、得意とする分野や取得できる時間の種類が微妙に異なります。
この違いを理解すると、後々スムーズに使い分けられるでしょう。
timeモジュールの概要
timeモジュール は、プログラムの開始時間や終了時間を手軽に取得したり、指定した秒数だけ処理を停止(スリープ)させたりできる便利なライブラリです。
Pythonを初めて触る方でも簡単に利用でき、ドキュメントもシンプルなので、まずはtimeモジュールから入る方も多いかもしれません。
ただし、実行環境やOSによっては time.time() が秒単位の精度しか持たない場合もあるため、厳密な計測には向いていないシーンがあります。
それでも日常的なスクリプトやちょっとした処理時間の計測では、十分に役立つでしょう。
time.time()の使い方
timeモジュールの代表的な関数が time.time() です。
これはエポック(通常1970年1月1日)からの経過時間を浮動小数点数で返します。
カウントの単位は秒です。
import time start = time.time() # 計測したい処理 result = 0 for i in range(1000000): result += i end = time.time() elapsed = end - start print("処理にかかった時間:", elapsed, "秒")
このコードでは、処理の前後で time.time()
を呼び出し、その差分を「経過時間」として求めています。
単純でわかりやすい方法であり、スクリプト全体がどれくらい動いたのかをざっくり測定するには適しています。
一方で、細かい精度を要する場面では誤差が大きくなる可能性がある点に注意しましょう。
ただ、初心者が最初に触れるには十分であり、「おおよそ○秒かかった」という情報が得られれば事足りるケースも少なくありません。
time.sleep()は計測時にはどう影響する?
time.sleep()
は指定した秒数だけスクリプトを停止させる関数です。
これ自体は計測用の機能ではありませんが、処理時間のモック(疑似的な遅延)を挿入したいときなどに試すことがあります。
たとえば、以下のように計測対象の処理に意図的に遅延を含めることで、本番環境に近い状況でのテストが行えるかもしれません。
import time start = time.time() # ダミーの処理を模擬 time.sleep(2) # 2秒間だけ止める end = time.time() elapsed = end - start print("ダミー処理の経過時間:", elapsed, "秒")
実務では、外部APIの応答待ちなどを再現する際にスリープを使うことがあります。
ただし、計測においては不要にスリープを混ぜると本来のコード性能が隠れてしまうため、何が本当の原因なのか分かりづらくなるケースも。
スリープを使う場合は、何を再現したいのかを明確にしましょう。
timeitモジュールの概要
timeitモジュール は、特定のコード片を何度も繰り返し実行することで実行時間の平均値や傾向を測定する仕組みを提供しています。
ループ回数やリピート回数を指定できるため、一度の偶然的な処理時間の偏りをなるべく排除できる利点があります。
簡単な使用方法としては、timeit.timeit()
関数に計測対象となるコードを文字列で渡し、実行回数などを指定します。
しかし、実務では文字列よりも実際の関数を引数に渡して測定したいケースが多いため、やや工夫が必要になることもあります。
timeit.timeit()の使い方
以下の例では、timeit.timeit()
に計測したい処理を文字列として与え、さらに前準備のコードは setup
パラメータを使って指定しています。
import timeit setup_code = """ import math """ test_code = """ result = 0 for i in range(100000): result += math.sqrt(i) """ # 1回あたりの計測を100回実行し、その結果を返す execution_time = timeit.timeit(stmt=test_code, setup=setup_code, number=100) print("合計実行時間:", execution_time, "秒")
ここでは、stmt
に測定対象のコード、setup
に前準備のコードを文字列として指定しています。
number=100
は「1回の計測で試行する回数」を表し、たとえばこれを 100
に設定すると、そのコードを100回繰り返してから1つの計測結果を得るイメージです。
timeitはデフォルトで何度かリピートする動きがありますが、パラメータ設定により回数を制御できるため、あらゆる場面で応用しやすいでしょう。
実務に近い形で書きたい場合は、timeit.Timer
クラスを使い、メソッドを直接呼び出して計測する方法があります。
この方がコードとして読みやすく、メンテナンスもしやすいかもしれません。
timeit.repeat()で複数回計測
timeit.repeat()
は、内部的に timeit.timeit()
を複数回呼び出し、その結果をリストとして返します。
これを使うと、計測を行うたびにどれくらいのばらつきがあるのかをより直感的に把握できます。
import timeit setup_code = """ import math """ test_code = """ result = 0 for i in range(100000): result += math.sqrt(i) """ # 5回リピートし、各リピートでtest_codeを100回実行 results = timeit.repeat(stmt=test_code, setup=setup_code, repeat=5, number=100) print("リピートごとの実行時間:", results) print("最小:", min(results), "最大:", max(results))
リピートごとの数値を比較すると、どの程度の誤差範囲で処理時間が変動しているかがわかります。
実務でアルゴリズムの最適化を図る際に、最小値や最大値も把握しておくと本当に改善したかどうか確認しやすいでしょう。
短時間で動くコードほど誤差に悩まされがちなので、複数回測定して平均やばらつきを見ることは有効な手段です。
perf_counterとprocess_time
perf_counter と process_time は、より正確な計測を行いたい場合に注目すべき関数です。
time.time()
はシステムクロックに依存するため、NTP同期や時刻合わせの影響を受けることがあります。
これに対し、perf_counter
や process_time
は単調増加する高精度タイマーに基づいて動作します。
どちらも time
モジュールの中に含まれていますが、用途が少し異なります。
perf_counterの特徴
perf_counter()
は、高解像度のモニタリング用タイマーから時刻を取得します。
計測開始と終了で perf_counter()
を呼び出せば、高精度な経過時間を取得できる点が特徴です。
import time start = time.perf_counter() # 計測したい処理 total = 0 for i in range(500000): total += i end = time.perf_counter() elapsed = end - start print("perf_counterで計測:", elapsed, "秒")
time.time()
と違い、システムの時刻変更の影響を受けにくいため、安定した計測が期待できます。
Pythonのドキュメントでも計測向けとして推奨されており、多くの開発者がパフォーマンス計測時に利用しています。
Webアプリケーションや大規模なスクリプトの処理速度を検証したいときは、まず perf_counter
を検討するとよいでしょう。
process_timeの特徴
一方で process_time()
は、プロセスがCPUを使用した時間を返す関数です。
つまり、プログラムが実際にCPUを占有していた時間だけを計測するイメージです。
import time start_cpu = time.process_time() # 計測したい処理 for i in range(1000000): pass end_cpu = time.process_time() cpu_time = end_cpu - start_cpu print("CPU使用時間:", cpu_time, "秒")
もし計測中に他のプロセスにCPUが奪われていたとしても、その分は含まれません。
そのため、システムの負荷状況によらず、純粋に自分のプログラムがどれだけCPUリソースを使っているかを知りたいときに便利です。
ただし、外部要因による待ち時間(たとえばI/O待ち)などは含まれませんので、総経過時間を知りたい場合は perf_counter
のほうが向いています。
実行時間を測定する際に気をつけたいポイント
時間計測は奥が深く、単に「開始時刻を取っておいて、終了時刻を差し引く」だけでは想定外の要素が混じることがあります。
たとえば、他のプロセスが同時に動いていてCPUやメモリを奪われているケースなどです。
ここでは、実行時間を測定する際に留意すべきポイントをいくつか挙げます。
計測環境の違い
同じPythonコードを実行しても、環境が異なれば計測結果が異なるのは珍しくありません。
ハードウェアスペック(CPUコア数やクロック数、メモリ速度など)、OSのスケジューリング、さらにはPythonインタープリタの実装などによっても差が出ます。
自宅のパソコンで書いたスクリプトがそこそこ速かったのに、いざクラウド上のサーバーで実行するとさらに速かったという例もあれば、逆に遅くなる例もあります。
そのため、単に数値を比較するだけでなく、「どの環境で計測した結果なのか」を整理しておくと混乱しにくいでしょう。
実務でも、ステージング環境と本番環境ではハードウェア構成が異なることがあるため、必ずしも同じ結果になるとは限りません。
計測結果のばらつき
プログラムの実行時間は、何度実行しても同じ値になるとは限りません。
とくに短い処理だと、数回の計測ですぐに誤差が出てしまうことがあります。
メモリ割り当てやキャッシュの状態、その他のプロセスによるCPUリソースの奪い合いなど、さまざまな要素が影響するからです。
ここで、先述した timeitモジュール のように、複数回計測して結果を平均化したり、最小・最大の範囲を見る方法が役立ちます。
一度きりの計測結果だけで結論を出すのは危険で、最低でも数回は繰り返すようにしたほうが安全です。
継続的に計測して動作状況をモニタリングする仕組みを導入すると、パフォーマンスの変化にいち早く気づけるかもしれません。
ライブラリや外部システムの影響
Pythonプログラム内で別のライブラリを呼び出す場合、そのライブラリが内部的にC言語などで実装されていることがあります。
この場合、Python部分だけ速くしてもライブラリ内部の待ち時間は短縮できない可能性がある点に注意が必要です。
また、外部のデータベースやAPIを使う場合は、その通信速度やサーバー側の状態にも大きく左右されます。
計測したい箇所が本当に「自分のコードの処理速度」なのか、それとも「外部システムとの通信時間」なのかをはっきりさせておかないと、対策を間違えるリスクが高まります。
必要に応じて、外部呼び出し部分と純粋なローカル処理部分を分けて計測すると良いでしょう。
具体的な使用例
ここでは、実務でよく目にするケースにスポットを当てながら、どんな手法が有効かを考えてみます。
単に「時間を測るだけ」で終わらず、どう活かすかを想像すると理解が深まるでしょう。
Webアプリでの応答速度測定
WebアプリケーションをPython(たとえばFlaskやDjango)で構築している場合、ユーザーからのリクエストに対してレスポンスを返すまでの時間を知りたいことが多いです。
このとき、ミドルウェアやフレームワークの機能を使ってもいいですが、簡易的にはリクエストの入り口と出口で時刻を記録して差分を取る方法が考えられます。
より正確に測りたいなら、perf_counter を用いるとよいでしょう。
また、アプリ内で特定のヘビープロセス(画像処理やデータ分析など)を実行する場合は、そこだけ別途 perf_counter
を挟んで計測すると、どの部分がボトルネックか判別しやすいです。
Webアプリは外部からのリクエスト数も多いため、実行結果にばらつきが出る場合があります。
そこで繰り返し測定し、平均や最大値などをモニタリングするアプローチが採られます。
import time from flask import Flask, request app = Flask(__name__) @app.route("/heavy_process") def heavy_process(): start = time.perf_counter() # ここで重い処理を実行 total = 0 for i in range(300000): total += i end = time.perf_counter() elapsed = end - start return f"処理結果: {total}, 所要時間: {elapsed}秒"
ユーザーからのリクエストが来るたびにこの関数が呼ばれます。
応答の中に経過時間を含めると、デバッグや性能テスト時に役立つことがあります。
ただし、本番環境ではセキュリティ上の理由やログ汚染を避けるため、必要に応じて削除したり、ログにのみ出力するなどの工夫が求められます。
機械学習モデルの学習時間測定
機械学習で大規模データセットを扱うと、学習プロセスが長時間にわたることもしばしばです。
その学習時間をどれくらい短縮できるかは、開発効率に大きく影響します。
たとえば、画像分類のタスクで大量の画像を学習させる場合、あらかじめ timeit や perf_counter を使って学習の開始前・終了後の時刻を取得すれば、学習時間をログとして残せます。
こうしておくと、アルゴリズムやハイパーパラメータ、GPUの使用有無などの条件によってどれくらい学習時間が変わったか、定量的に把握できます。
import time def train_model(model, data): start = time.perf_counter() # 実際の学習処理を呼び出す model.fit(data) end = time.perf_counter() return end - start # 仮の例 elapsed = train_model(my_model, training_data) print("学習にかかった時間:", elapsed, "秒")
大規模な学習になると数分どころか数時間かかるケースもあります。
もしGPUリソースを使う場合、GPUの混雑具合などにも左右されることがあります。
複数回繰り返して計測すれば、どの程度の誤差が生じるかを確認しやすいでしょう。
バッチ処理での実行時間検証
定期的に走るバッチ処理(夜間に行うデータ集計やレポート生成など)でも、実行時間の把握は大切です。
処理が長期化すると、朝になっても終わらない、というトラブルを招く可能性があるからです。
このような場合、処理の冒頭と最後で perf_counter
を呼び出すことが多いでしょう。
また、サブタスクが複数ある場合は、各サブタスクごとに経過時間を計測してログに記録する仕組みを入れておくと、どのタスクが一番時間を食っているのかすぐに分かります。
たとえば、バッチ処理の中で何段階かのデータ前処理やファイル書き出し、ネットワーク転送などがある場合、それぞれの開始時刻と終了時刻を控えるように実装するとよいでしょう。
これだけで日々のログからパフォーマンス低下の兆候を読み取れるかもしれません。
よくあるエラーやトラブルと対処法
時間計測を行ううえで意外と多いのが、ちょっとした使い方のミスや誤解によるエラー・トラブルです。
次に挙げる事例を頭に入れておけば、スムーズに計測作業を進めやすくなります。
timeモジュール関連のトラブル
time.time()
を使った計測で、実行結果が妙に大きな値になったり、負の値になったりすることはあまりありません。
しかし、OSによっては秒の分解能が十分でないため、小さい差分を測るには誤差が大きくなることがあります。
また、NTPによる時刻修正のタイミングで、計測中にシステムクロックが急に進んだり戻ったりすると、思わぬ値を取得するケースもゼロではありません。
こうしたリスクを避けたい場合、perf_counter に乗り換えるのが有効です。
timeモジュールでは time.monotonic()
という単調増加するタイマーも用意されていますが、初学者には perf_counter
を使うほうが分かりやすいでしょう。
timeitモジュール関連のエラー
timeit
を使うときにありがちな間違いは、計測対象のコードで必要なモジュールのインポートを setup
に入れ忘れることです。
たとえば math.sqrt()
を呼び出したいのに setup
パラメータに import math
を書くのを忘れると、NameError
が発生します。
また、計測対象が複数の関数にまたがる場合、文字列で指定するには少々複雑になることがあります。
そのようなときは、timeit.Timer
を使って測定を行うか、あるいは関数をまとめた上で文字列から呼び出す方法を検討してください。
いずれにしても、測定対象と前準備の切り分け がポイントです。
perf_counterやprocess_timeでの罠
perf_counter
は時刻を取得する関数ではなく、あくまで「高解像度のカウント」を返すものです。
これを時計代わりに使おうとしても、何年何月何日の何時何分なのかは分かりません。
「経過時間を測る目的」にのみ使うようにしましょう。
process_time
は先述の通り、CPUを使用している時間しかカウントされません。
もしコードがI/O待ちでほとんどCPUを使っていない状態だと、実際の壁掛け時間(処理開始から終了までの人間が感じる時間)とは乖離が生じる可能性があります。
計測結果が予想と大きく違う場合、どの指標を測定しているかを再確認してみてください。
timeモジュールのsleepや外部I/Oが含まれる処理を計測するならperf_counterを、CPU処理だけを純粋に計測したいならprocess_timeを使う、といった使い分けを意識してみましょう。
まとめ
Pythonでの時間計測は、シンプルな time.time()
から高精度な perf_counter
、そして特定のコード片を繰り返し評価できる timeit
まで、さまざまな手段があります。
どの手段を選ぶかは、「何を測りたいのか」「どの程度の精度が必要か」「環境依存の影響をどう扱うか」で変わってくると考えられます。
初心者の方は time.time()
でざっくりとした経過時間を調べるところから始めることが多いですが、もう一歩踏み込むなら perf_counter のような高精度タイマーが便利でしょう。
一方、プログラム全体ではなく特定のコードブロックの平均実行時間を知りたいときは timeit を活用するのが定番です。
実務での応用を考えると、計測結果がばらつく要因や、外部システムとの通信時間をどう切り離すかといった点が重要になります。
何度か繰り返し計測してから平均値を取る、計測範囲を限定するなどのテクニックを組み合わせて、最適なアプリケーションを作り上げてみてください。
みなさんが本記事を通して、Pythonでの時間計測方法や応用例をイメージできるようになり、実務や学習に役立てるきっかけになればうれしいです。
効率的で分かりやすいコードを目指すためにも、まずは一度、自分の書いた処理がどれくらいの時間を要しているのかを図ってみてはいかがでしょうか。