Python | データ構造強化:速度計測(timeit)

Python Python
スポンサーリンク

概要(timeit は「公平に何度も走らせて平均速度を測る」ための標準ツール)

timeit は、コードの実行時間を正確に測るための標準モジュールです。1回だけの計測はノイズに弱いので「複数回・同じ条件で」走らせて平均をとるのが基本設計。Jupyter、スクリプト、コマンドラインのどれでも使えます。重要なのは、測りたい“処理だけ”を切り出して、準備(セットアップ)と計測本体を分けることです。順序最適化と回数設定で再現性が高くなります。

基本の使い方(ここが重要)

スクリプトから timeit.timeit を使う

関数やスニペットを一定回数繰り返して、合計秒数を返します。処理が短いほど number を大きく、長いほど小さくします。

import timeit

code = "sum(range(1000))"
sec = timeit.timeit(code, number=10000)
print(sec)  # 10000回合計の秒数(1回あたりは sec/number)
Python

Python の文字列として渡したコードは、現在のスコープの関数や変数を参照できるように globals を渡すと便利です。

import timeit

def f(n): 
    return sum(range(n))

sec = timeit.timeit("f(1000)", globals=globals(), number=10000)
print(sec)
Python

セットアップと本体を分ける(初期化コストを外に出す)

準備に時間がかかる場合、set up で一度だけ実行し、計測本体では“純粋な処理”だけを測ります。

import timeit

setup = "data = list(range(1000))"
stmt  = "sum(data)"
sec = timeit.timeit(stmt, setup=setup, number=100000)
print(sec)
Python

コマンドラインから測る(-m timeit)

コマンドライン実行は短い比較に便利です。-n は1回の試行の実行回数、-r は試行の繰り返し回数で、最小値を報告します。

python -m timeit -n 100000 "sum(range(100))"
python -m timeit -n 1000 -r 7 -s "data=list(range(1000))" "sum(data)"
Python

重要ポイントの深掘り(公平性・回数設定・計測の切り分け)

“測りたい処理だけ”を測定対象にする

計測対象に準備やI/Oが混ざると、純粋な計算速度が見えません。セットアップで一度準備し、本体は処理だけにします。これで比較が公平になります。

# 悪い例:準備も一緒に測ってしまう
timeit.timeit("data=list(range(1000)); sum(data)", number=100000)

# 良い例:準備を setup に分離
timeit.timeit("sum(data)", setup="data=list(range(1000))", number=100000)
Python

number(回数)と r(反復)の考え方

処理が非常に高速なら number を増やして測定値を安定化します。遅い処理なら number を小さくして、r(反復)を増やし最良値を採用するとノイズに強くなります。1回あたりの平均時間は合計秒数を number で割って算出します。

sec = timeit.timeit("sum(range(1000))", number=10000)
avg = sec / 10000
print(avg, "秒/回")
Python

比較は「同じセットアップ・同じ回数」で

比較したい候補間で setup と number を揃え、測定方法を同一にします。順序入れ替えや複数回の計測でばらつきを確認し、極端な外れ値は無視します。

実務の使いどころ(アルゴリズム比較・データ構造選定・マイクロ最適化)

アルゴリズム比較(内包表記 vs ループ)

同じ仕事をする2つの実装を、同一条件で比較します。差が小さいときは読みやすさ優先、差が大きいときは速い方を採用します。

import timeit

setup = "data=list(range(1000))"
c1 = timeit.timeit("[x*x for x in data]", setup=setup, number=5000)
c2 = timeit.timeit("""
out=[]
for x in data: out.append(x*x)
""", setup=setup, number=5000)

print("comp:", c1, "loop:", c2)
Python

データ構造選定(list vs deque の先頭取り出し)

操作の特性が速度に直結します。先頭操作は deque が強いことを数値で確認すると、選定が確信に変わります。

import timeit

setup_list  = "from collections import deque; L=list(range(10000))"
stmt_list   = "L.pop(0)"
setup_deque = "from collections import deque; D=deque(range(10000))"
stmt_deque  = "D.popleft()"

t_list  = timeit.timeit(stmt_list,  setup=setup_list,  number=10000)
t_deque = timeit.timeit(stmt_deque, setup=setup_deque, number=10000)

print("list pop(0):", t_list)
print("deque popleft():", t_deque)
Python

キー計算の前取り(attrgetter/itemgetter の効果)

sorted の key をラムダから前取りへ変える効果を測って、読みやすさとのバランスを判断します。

import timeit
setup = """
from operator import itemgetter
rows=[('a', i) for i in range(1000)]
"""
s_lambda = "sorted(rows, key=lambda r: r[1])"
s_getter = "sorted(rows, key=itemgetter(1))"

t1 = timeit.timeit(s_lambda, setup=setup, number=5000)
t2 = timeit.timeit(s_getter, setup=setup, number=5000)
print("lambda:", t1, "itemgetter:", t2)
Python

よくある落とし穴の回避(環境差・キャッシュ・I/O混在)

計測の再現性は“環境”に依存する

CPU負荷、バックグラウンドプロセス、電源設定で結果が揺れます。余計な負荷を減らし、同じ環境・同じ条件で複数回測定し、中央値や最小値を参考にします。

ウォームアップとキャッシュの影響を意識する

初回はインタプリタやモジュールの読み込み、キャッシュ未構築で遅くなることがあります。timeit は内部で繰り返すため自然に慣れますが、セットアップに import を入れておく、比較対象の順序を入れ替えると偏りを避けられます。

I/O を混ぜない(純粋な計算だけを測る)

ファイル・ネットワーク・print は環境差が大きく、計測ノイズになります。計算速度を測りたいなら I/O をセットアップに逃がすか、専用のベンチを用意します。

例題で身につける(定番から一歩先まで)

例題1:1回あたりの平均時間を出す

import timeit
sec = timeit.timeit("sum(range(1000))", number=10000)
print("total:", sec, "avg:", sec/10000)
Python

例題2:関数を直接計測(callable を渡す)

文字列ではなく、callable を渡すとスコープ管理が簡単です。

import timeit

def task():
    return sum(range(1000))

sec = timeit.timeit(task, number=10000)
print(sec)
Python

例題3:複数行の処理を比較

import timeit

setup = "data=list(range(1000))"
A = """
total = 0
for x in data:
    total += x
"""
B = "sum(data)"

tA = timeit.timeit(A, setup=setup, number=10000)
tB = timeit.timeit(B, setup=setup, number=10000)
print("loop:", tA, "sum:", tB)
Python

例題4:Jupyter なら %%timeit マジックで手早く

# セルの先頭に書くと、そのセルのコードを自動で複数回計測
%%timeit
sum(range(1000))
Python

計測回数やリピート回数は自動調整され、平均・標準偏差が表示されます。

まとめ

timeit は「同条件で何度も走らせ、純粋な処理だけを測って比較する」ための標準ツールです。セットアップと本体を分け、number と反復で安定化し、平均時間を算出する。比較は同じ条件で、公平に。I/Oや初期化を含めないことで“計算そのものの速さ”が見えます。アルゴリズム比較、データ構造選定、キー計算の前取りなど、迷ったら数値で決める癖をつける。測って、確かめて、最適化の手応えを得る—そのサイクルが、短くて速くて壊れないコードを作ります。

タイトルとURLをコピーしました