Python

「x > 0 and x < 100」と「0 < x < 100」ってどっちの書き方するのがいいか試してみた

はじめに

Pythonでは、

if x > 0 and x < 100:

みたいな範囲条件を以下のように書けます。

if 0 < x < 100:

英語っぽく書けるようにって配慮でしょうね。
ふとどっちの方が速いのか気になりました。

ともかく測ってみよう

計測用コード

perf.py
import random
import time
import numpy as np

random.seed(1234)
arr = []
for _ in range(10):
    s = time.time()
    for _ in range(1000 * 1000):
        x = random.random()
        if x > 0.2 and x < 0.8:
            pass
    e = time.time()
    arr.append(e - s)
print(np.average(arr))

random.seed(1234)
arr = []
for _ in range(10):
    s = time.time()
    for _ in range(1000 * 1000):
        x = random.random()
        if 0.2 < x < 0.8:
            pass
    e = time.time()
    arr.append(e - s)
print(np.average(arr))

実行結果

0.322909283638
0.334062027931

実行ごとに差はあるけど自分でand書いた方が速い。

どう実行されるのか見てみよう

「x > 0 and x < 100」と「0 < x < 100」がそれぞれどういう命令コードになるのか見てみます。

>>> import dis
>>> co1 = compile('''
... x = 50
... x > 0 and a < 100
... ''', '', 'exec')
>>> dis.dis(co1)
  2           0 LOAD_CONST               0 (50)
              2 STORE_NAME               0 (x)
  3           4 LOAD_NAME                0 (x)
              6 LOAD_CONST               1 (0)
              8 COMPARE_OP               4 (>)
             10 JUMP_IF_FALSE_OR_POP    18
             12 LOAD_NAME                0 (x)
             14 LOAD_CONST               2 (100)
             16 COMPARE_OP               0 (<)
        >>   18 POP_TOP
             20 LOAD_CONST               3 (None)
             22 RETURN_VALUE
>>> co2 = compile('''
... x = 50
... 0 < x < 100
... ''', '', 'exec')
>>> dis.dis(co2)
  2           0 LOAD_CONST               0 (50)
              2 STORE_NAME               0 (x)
  3           4 LOAD_CONST               1 (0)
              6 LOAD_NAME                0 (x)
              8 DUP_TOP
             10 ROT_THREE
             12 COMPARE_OP               0 (<)
             14 JUMP_IF_FALSE_OR_POP    22
             16 LOAD_CONST               2 (100)
             18 COMPARE_OP               0 (<)
             20 JUMP_FORWARD             4 (to 26)
        >>   22 ROT_TWO
             24 POP_TOP
        >>   26 POP_TOP
             28 LOAD_CONST               3 (None)
             30 RETURN_VALUE

xが50とした場合、「0 < x < 100」の方がROT_THREE, JUMP_FORWARD分、命令が多いです。これが「0 < x < 100」の方が遅い理由と思われます。1

まとめ

というわけで「x > 0 and x < 100」って書くのと「0 < x < 100」って書くのどちらが速いか、何故速いのかを見てきました。前者の方が速く、後者が遅いのは余分な命令が実行されるからなようです。

ここでちゃぶ台ひっくり返しますが、個人的には後者の書き方の方が好みです。100万回ループして0.01秒しか違わないのなら人間が読みやすい方を選ぶべきです。

余談

身近にあるいくつかの環境で試してたのですがたまに「0 < x < 100」式の方が速い環境がありました。gccの違い?関係あるのかな。

余談その2

言うまでもないかもしれませんが、大体の言語では「0 < x < 100」と書くと予想に反する結果になります。初心者の人がついついこう書いてしまいがちですよね。

多くの言語では、

x = 200
0 < x < 100 → (0 < x) < 100 → true < 200 → true

となってしまいます。Javaみたいにbooleanとintの比較ができない言語だとコンパイル時点でエラーになったかな。


  1. 「x > 0 and x < 100」と「0 < x < 100」を見比べた場合、前者ではLOAD_NAME、後者はDUP_TOPでxの値をスタックに積んでいます。命令単体としてはLOAD_NAMEの方が時間はかかると思います。