Home/Learn/Python A–Z/Bytecode & How CPython Runs Your Code

Bytecode & How CPython Runs Your Code

Advanced
Internals & Performance

CPython compiles source to bytecode (inspect it with dis), caches it in __pycache__, and executes it in the interpreter loop — since 3.11, an adaptive specializing interpreter rewrites hot bytecode for big speedups.

Overview

Python is not "purely interpreted": your .py file is first compiled to bytecode — instructions for CPython's stack-based virtual machine — then that bytecode is executed by the interpreter loop. The dis module shows the exact instructions, which is the ground truth for questions like "is x += 1 atomic" or "why are local variables faster than globals" (LOAD_FAST is an array index; LOAD_GLOBAL is dict lookups). Compiled bytecode is cached as .pyc files in __pycache__, which is why the second import of a module is faster. Since Python 3.11 the interpreter is adaptive: hot instructions specialize themselves to observed types, a major reason 3.11+ is 25-60% faster — upgrading Python versions is itself a performance optimization.

dis — Reading the Bytecode

dis.dis shows what a function compiles to. Locals use LOAD_FAST (indexed array access); globals use LOAD_GLOBAL (namespace lookup) — the mechanical reason hot loops run faster on local variables.

Bytecode answers atomicity and locals-vs-globals questions
import dis

def total_marks(marks):
    total = 0
    for m in marks:
        total += m
    return total

dis.dis(total_marks)
#   LOAD_CONST     1 (0)          ← push 0
#   STORE_FAST     1 (total)      ← total is slot 1: array access, fast
#   LOAD_FAST      0 (marks)
#   GET_ITER
#   FOR_ITER      ...
#   LOAD_FAST      1 (total)      ← load, add, store: three separate steps
#   LOAD_FAST      2 (m)
#   BINARY_OP     13 (+=)
#   STORE_FAST     1 (total)      ← a thread could be switched mid-sequence:
#   ...                             this is WHY += is not atomic

import math
def use_global():  return math.pi        # LOAD_GLOBAL — dict lookups
def use_local():
    pi = math.pi                         # bind once...
    return pi                            # LOAD_FAST — array index

.pyc Caching and the 3.11+ Adaptive Interpreter

Bytecode compilation happens once per source change; __pycache__ stores the result keyed by interpreter version. From 3.11, hot code paths specialize: generic instructions rewrite themselves for the types actually seen, and 3.12/3.13 build on this with a JIT foundation.

Compile once, cache in __pycache__, specialize when hot
# __pycache__/utils.cpython-312.pyc  ← compiled bytecode cache
#   - created on first import, reused while source is unchanged
#   - source mtime/hash checked → edit .py and it recompiles automatically
#   - version-tagged: 3.12 and 3.13 caches coexist side by side
#   - this is compile-to-bytecode caching, NOT machine-code compilation

# The specializing adaptive interpreter (PEP 659, Python 3.11+):
def add(a, b):
    return a + b

for _ in range(100):
    add(2, 3)
# After warm-up, generic BINARY_OP specializes to BINARY_OP_ADD_INT —
# a fast path that skips type dispatch while both args stay ints.
# Specializations deoptimize automatically if types change: add("a", "b") still works.

# Practical takeaways:
#   - Upgrading 3.10 → 3.12 is often a free 25-60% speedup
#   - Type-stable hot loops (same types every iteration) specialize best
#   - python -X importtime app.py   → per-module import cost breakdown
#   - Python 3.13+: experimental copy-and-patch JIT builds on these tiers

Key Points to Remember

  • 1Source compiles to bytecode for a stack VM; dis.dis shows the instructions
  • 2LOAD_FAST (locals, array index) beats LOAD_GLOBAL (dict lookup) — locals are faster
  • 3__pycache__/.pyc caches bytecode per interpreter version; auto-invalidates on edit
  • 4PEP 659 adaptive specialization makes 3.11+ dramatically faster — upgrades are free perf

Interview Questions

Sign in to ask Aria
1

Is Python compiled or interpreted? Explain what actually happens to a .py file.

MediumMicrosoft
2

Use dis to prove that x += 1 is not thread-safe.

HardGoogle
3

Why did many services get 30-50% faster just by moving to Python 3.11+?

HardRazorpay

Ask Aria about Bytecode & How CPython Runs Your Code

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…