multiprocessing and Process
coreintermediatemultiprocessing runs code in separate OS processes instead of threads. Each process gets its own Python interpreter and its own memory — so unlike threading, multiple processes really do run CPU-bound work at the same time.
Think of it as
Threading is several cooks sharing one kitchen, taking turns at the one stove (the GIL). Multiprocessing is giving each cook their own separate kitchen — they can all cook at once, but now ingredients (data) have to be physically carried between kitchens instead of just handed across the counter.
What we're doing: Run the same CPU-bound function serially (twice, back to back) and then across a 2-process Pool, timing both, to see multiprocessing actually use more than one CPU core.
- 16
- pool.map hands one N to each of the 2 worker processes and runs both cpu_heavy calls genuinely at the same time, on separate cores.
- 17
- Starting worker processes and pickling the function/arguments to them is real, measurable overhead — it eats into the speedup a short-lived task would otherwise show.
serial: 0.554s, parallel(2 procs): 0.514s, results match: TrueWhy this works: The two runs finish in close to the same wall-clock time here, not because multiprocessing failed, but because starting two fresh processes has real fixed overhead (each gets its own interpreter startup) that eats most of this task's modest speedup. For CPU work that runs long enough to dwarf that startup cost, the parallel version pulls ahead — this is the honest, measured shape of that tradeoff, not an idealized 2x.
A target function that isn't defined at module level can't be sent to a child process
Wrong
Better
What you see: _pickle.PicklingError: Can't pickle local object <function outer.<locals>.local_worker at 0x...>
Why: A Process target is sent to the child by pickling it — but pickle stores a function by its module path and name, not its actual code. A function defined inside another function has no importable module-level name, so pickle cannot reconstruct a reference to it in the child process. Moving the target to module level fixes it immediately.
- Process A — own interpreter, own GIL, own memory
- pickle over a pipe — the only way data crosses
- Process B — own interpreter, own GIL, own memory
Pool methods for handing out work
Together
Remember: multiprocessing runs real, separate processes — genuine CPU parallelism, at the cost of pickling data across the process boundary and real startup overhead.
See also: cpu bound vs io bound workloads · multiprocessing ipc and serialization · gil effects and when to use what

