今回は、Pythonでプログラムの実行時間を計測する方法についてご紹介します。
プログラムのパフォーマンスを把握する上で、実行時間の計測は重要な作業です。
処理速度が遅ければユーザー体験を損ねる可能性があり、ビジネス的な損失につながるケースもあります。
ここでは、timeモジュールやtimeitモジュール、さらにはデコレータやコンテキストマネージャを使った高度な計測方法まで、幅広く解説します。
timeモジュールを使った基本的な計測
まずは、timeモジュールによる計測方法を紹介します。
Pythonには、time.perf_counter()・time.time()・time.process_time()など、状況に応じて使い分けができる関数があります。
time.perf_counter() を使う方法
time.perf_counter()は、システムの高精度なパフォーマンスカウンタを利用するため、短い処理や細かな計測に最適です。
以下の例では、ループ処理に要する実行時間を計測しています。
1 2 3 4 5 6 7 8 |
import time start = time.perf_counter() # --- 計測したい処理 --- result = sum(i * i for i in range(1000000)) end = time.perf_counter() print(f"実行時間: {end - start:.6f} 秒") |
実行時間: 0.123456 秒
この例では、time.perf_counter()でスタート時刻を取得し、処理が終わった後でもう一度呼び出し、差分を計算しています。
精度が高く、待機時間やスリープなども含んだ「経過時間」を取得できるため、最もよく利用される計測方法のひとつです。
time.time() と time.process_time() の使い分け
time.time()は、UNIXエポック(1970年1月1日からの経過秒)を返す関数です。
プログラムが開始してからの「実時間」(ウォールクロック時間)を計測したい場合に使用します。
一方、time.process_time()はプロセスがCPUを実際に使用した時間(CPU時間)のみを計測します。
計算処理の負荷を知りたいときには、待機時間を含まないこの関数が有効です。
次のコードは、両方を使って実行時間を計測する例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import time # 全体の経過時間 start_wall = time.time() result = sum(i * i for i in range(1000000)) end_wall = time.time() # CPU使用時間のみ start_cpu = time.process_time() result = sum(i * i for i in range(1000000)) end_cpu = time.process_time() print(f"wall-clock時間: {end_wall - start_wall:.6f} 秒") print(f"CPU時間: {end_cpu - start_cpu:.6f} 秒") |
wall-clock時間: 0.198765 秒
CPU時間: 0.150321 秒
wall-clock時間は待機やI/Oなども含めた実世界の経過時間を表し、CPU時間は純粋にCPUで動作していた時間を表します。
timeitモジュールを利用した計測
timeitモジュールは、短いコードの実行時間を正確に測定するための標準ライブラリです。
内部で複数回実行して平均を取る仕組みを持っているため、外れ値の影響を受けにくいという特徴があります。
プログラム内でtimeitを使う例
timeitモジュールを活用すると、特定のコードブロックだけを繰り返し実行して測定できます。
次は、100回実行して合計時間を測定し、その平均を表示する例です。
1 2 3 4 5 6 7 8 9 10 11 12 |
import timeit code_to_test = """ result = [i * i for i in range(100000)] """ setup_code = "pass" # 100回実行して合計時間を取得 elapsed = timeit.timeit(stmt=code_to_test, setup=setup_code, number=100) print(f"100回実行した平均時間: {elapsed / 100:.6f} 秒") |
100回実行した平均時間: 0.012345 秒
より正確な評価を行うために、回数(number)は状況に応じて調整してください。
また、Jupyter NotebookやIPythonのインタラクティブ環境では、%timeitマジックコマンドが利用でき、さらに手軽に計測できます。
デコレータやコンテキストマネージャを使った応用例
コード全体や特定の関数に対して、繰り返し計測コードを書くのが煩雑な場合は、デコレータやコンテキストマネージャを活用できます。
実行時間計測用デコレータ
以下のデコレータを使うと、対象の関数を呼ぶだけで自動的に実行時間を測定できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import time import functools def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() print(f"{func.__name__}の実行時間: {end - start:.6f} 秒") return result return wrapper @timer def example_function(n): return sum(i * i for i in range(n)) # 実行例 example_function(1000000) |
example_functionの実行時間: 0.432109 秒
このように、実行時間を計測したい関数に対して@timerというアノテーションをつけるだけで済むため、保守性が向上します。
コンテキストマネージャを使う方法
コードブロック全体の実行時間を測りたい場合は、コンテキストマネージャを定義する方法もあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import time class Timer: def __enter__(self): self.start = time.perf_counter() return self def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.perf_counter() self.interval = self.end - self.start print(f"ブロック全体の実行時間: {self.interval:.6f} 秒") # 使用例 with Timer(): result = sum(i * i for i in range(1000000)) |
ブロック全体の実行時間: 0.567890 秒
複数行にわたる処理全体を簡潔に計測できるため、特に長めの処理をまとめて測定したい場合に便利です。
よくあるエラーや注意点
importを忘れてNameErrorが発生するケースが挙げられます。
timeモジュールやtimeitモジュールを使用する際は、冒頭にimport time、import timeitなどを忘れずに記述してください。
AttributeErrorが出る場合は、使用しているPythonバージョンが古く、time.perf_counter()が存在しない可能性があります。
また、計測結果は常に環境依存です。
バックグラウンドで動いているプログラムやガベージコレクションのタイミングで処理速度は変動するため、1回だけの測定値を過信しないようにしましょう。
正確に評価するには、複数回の測定を行い、平均や分散を見て判断することが推奨されます。
まとめ
Pythonの実行時間を計測する方法は、timeモジュールやtimeitモジュールを使うのが一般的です。
処理内容に応じて、perf_counterやprocess_time、デコレータやコンテキストマネージャなどを組み合わせると効率的に測定できます。
複数回実行や環境依存性を考慮しながら、最適な計測方法を選びましょう。