pythonperformancecpythonmicro-optimizationpython-internals

Why is `if x is None: pass` faster than `x is None` alone?


Timing results in Python 3.12 (and similar with 3.11 and 3.13 on different machines):

When x = None:
13.8 ns  x is None
10.1 ns  if x is None: pass

When x = True:
13.9 ns  x is None
11.1 ns  if x is None: pass

How can doing more take less time?

Why is if x is None: pass faster, when it does the same x is None check and then additionally checks the truth value of the result (and does or skips the pass)?

Times on other versions/machines:

Benchmark script (Attempt This Online!):

from timeit import repeat
import sys

for x in None, True:
    print(f'When {x = }:')
    for code in ['x is None', 'if x is None: pass'] * 2:
        t = min(repeat(code, f'{x=}', repeat=100))
        print(f'{t*1e3:4.1f} ns ', code)
    print()

print('Python:', sys.version)

Solution

  • Look at the disassembled code:

    >>> import dis
    >>> dis.dis('if x is None: pass')
      0           0 RESUME                   0
    
      1           2 LOAD_NAME                0 (x)
                  4 POP_JUMP_IF_NOT_NONE     1 (to 8)
                  6 RETURN_CONST             0 (None)
            >>    8 RETURN_CONST             0 (None)
    >>> dis.dis('x is None')
      0           0 RESUME                   0
    
      1           2 LOAD_NAME                0 (x)
                  4 LOAD_CONST               0 (None)
                  6 IS_OP                    0
                  8 RETURN_VALUE
    

    The if case has a special POP_JUMP_IF_NOT_NONE operation, which is faster than a LOAD_CONST plus IS_OP. You can read the detailed discussion about it here: https://github.com/faster-cpython/ideas/discussions/154.