Four workloads run in one CPython process: parsing 200 MB of JSON, waiting on 500 HTTP requests, multiplying two 4000 by 4000 NumPy matrices, and compressing 2 GB with a C library that releases the interpreter lock. Say which of them speed up with four threads and why.

Four workloads run in one CPython process: parsing 200 MB of JSON, waiting on 500 HTTP requests, multiplying two 4000 by 4000 NumPy matrices, and compressing 2 GB with a C library that releases the interpreter lock. Say which of them speed up with four threads and why.

Approach: For each workload ask whether the work happens while the interpreter lock is held, and what a C extension is permitted to do around a long call.

The HTTP waits, the matrix multiply and the compression all speed up, and the JSON parsing does not, because the global interpreter lock is held whenever Python bytecode execution happens and is released around blocking input and output and around long C calls that drop it explicitly. The standard library JSON parser is a C routine that holds the lock for its whole run, so four threads share one interpreter and the parse stays single threaded. Socket reads release the lock while waiting, so 500 requests overlap on one core. NumPy releases it inside the underlying BLAS call, so the multiply uses every core. The compression library releases it by contract, so it scales the same way. The lock protects interpreter state including reference counts, so it serialises bytecode and never constrains work done outside the interpreter.

Follow-up: What does a free-threaded build change about the JSON case, and what does it cost single-threaded code?

Key concepts: global interpreter lock, bytecode execution, releasing the lock, input and output.