3. ベタ書きと関数呼び出し

ベタ書きの方が速い

Python は関数やメソッドの呼び出しコストが結構大きいので、 内部で何度も呼び出されている関数を単純にベタ書きするだけで、 処理速度が少し速くなったりします。 cProfile で cumtime が大きかった時の対処法の1つです。

3.1. max 関数の書き換え

4, 5 倍速くなる。

比較対象

max(2, 1)
2 if 2 > 1 else 1

測定結果

# ベタ書きのコード
python -m timeit -n 1000 "max(2, 1)"

# 関数を使ったコード
python -m timeit -n 1000 "2 if 2 > 1 else 1"

$ # ベタ書きのコード
$ python -m timeit -n 1000 "max(2, 1)"
1000 loops, best of 3: 0.234 usec per loop
$ # 関数を使ったコード
$ python -m timeit -n 1000 "2 if 2 > 1 else 1"
1000 loops, best of 3: 0.0379 usec per loop

補足

ちなみに三項演算子では「定数畳み込み」はされない。

import dis
dis.dis("2 if 2 > 1 else 1")
>>> import dis
>>> dis.dis("2 if 2 > 1 else 1")
  1           0 LOAD_CONST               0 (2)
              2 LOAD_CONST               1 (1)
              4 COMPARE_OP               4 (>)
              6 POP_JUMP_IF_FALSE       12
              8 LOAD_CONST               0 (2)
             10 RETURN_VALUE
        >>   12 LOAD_CONST               1 (1)
             14 RETURN_VALUE
>>> 

3.2. 切り捨て

4, 5 倍速くなる。

比較対象

4 // 3
math.floor(4 / 3)
int(4 / 3)

測定結果

python -m timeit -s "a, b = 4, 3"              "a // b"
python -m timeit -s "a, b = 4, 3"              "int(a / b)"
python -m timeit -s "a, b = 4, 3; import math" "math.floor(a / b)"

$ python -m timeit -s "a, b = 4, 3"              "a // b"
10000000 loops, best of 5: 35.6 nsec per loop
$ python -m timeit -s "a, b = 3, 3"              "int(a / b)"
2000000 loops, best of 5: 172 nsec per loop
$ python -m timeit -s "a, b = 4, 3; import math" "math.floor(a / b)"
2000000 loops, best of 5: 163 nsec per loop
$ 

補足

4 / 3, 4 // 3 には、定数畳み込みが走るので -s オプションで変数に代入している。

3.3. 組み込み関数 divmod の書き換え

比較対象

c, d = 4//3, 4%3
c, d = divmod(4, 3)

測定結果

python3 -m timeit -s "a,b=5,3" "divmod(a, b)"
python3 -m timeit -s "a,b=5,3" "a//b"
$ python3 -m timeit -s "a,b=5,3" "divmod(a, b)"
2000000 loops, best of 5: 127 nsec per loop
$ python3 -m timeit -s "a,b=5,3" "a//b"
10000000 loops, best of 5: 35.3 nsec per loop
$ 

補足

3.2, 3.3 については「暗黙の型変換」を題材に記事を書きました。