Stop by Stop

Concurrency line

Concurrency vs parallelism

A kitchen can speed up by juggling dishes or by adding burners, and the two fix different problems.

Words to know

Stops explain each step. The film plays the whole ride.

Explain it
Route
Two burnersDishes to cookDishes A to DBurner 1Cooking ABurner 2Nothing cookingThe ovenDishes wait hereReady to serve0 dishes done
  • Start cooking
  • Set aside
  • Ready or done
  • Stuck
Stop 1 of 9

Welcome to the kitchen

Welcome to the kitchen. Dishes wait to be cooked, a burner is where cooking happens, and a cook works one burner. The cook starts dish A.

Next stop: Dish A has to bake

All stops

The everyday version

Think of a kitchen. One cook juggling two dishes is concurrency; two cooks working side by side is parallelism.

  • Dishes to cookis the Task queue

    The pile of dishes waiting to be started.

  • Burner 1 and its cookis the CPU core 1

    Has hands for one thing at a time, but can switch dishes when one is waiting.

  • Burner 2 and a second cookis the CPU core 2

    An independent pair of hands. Together the two can really work at the same instant.

  • The ovenis the Waiting area

    A dish sits here while it bakes. Waiting needs no cook at all.

  • Ready to serveis the Finished

    Where finished dishes are handed out.

  • One chef’s hatin a Python kitchen

    You hired two cooks, but they share one chef’s hat. Only the wearer may cook, so the second cook stands idle.

Interview questions

Say the short answer first. Go deeper only if they ask.

What is the difference between concurrency and parallelism?
Short answer

Concurrency is managing many tasks in progress. Parallelism is executing many at the same instant.

If they dig deeper

Concurrency is about structure and works on one core by interleaving. Parallelism needs several cores. A system can be either, or both.

Answer that loses points

“They are the same thing” or “parallel just means faster concurrent”.

Can you have concurrency on a single core?
Short answer

Yes, by interleaving tasks at waits or by time-slicing.

If they dig deeper

The OS schedules threads on a core in preemptive time slices. asyncio switches cooperatively at await. Several tasks progress, but never two instruction streams at once.

Answer that loses points

“No, you need multiple cores.”

Which problems benefit from concurrency, and which from parallelism?
Short answer

I/O-bound work benefits from concurrency. CPU-bound work benefits from parallelism.

If they dig deeper

Waiting tasks leave the CPU idle, so interleaving hides latency. CPU-bound tasks have no idle time, so only more cores help.

Answer that loses points

“Add threads to speed up any slow code.”

Do Python threads run in parallel?
Short answer

Not for pure-Python bytecode on the default GIL build. Yes for I/O waits and GIL-releasing C code.

If they dig deeper

Only one thread holds the GIL, but blocking I/O releases it, so I/O threads overlap. Free-threaded builds, officially supported in 3.14 but optional, allow parallel bytecode.

Answer that loses points

“Python cannot do threads or parallelism at all.”

How would you speed up a CPU-bound Python function across 8 cores?
Short answer

Use ProcessPoolExecutor or multiprocessing, or a library that releases the GIL.

If they dig deeper

Each process has its own interpreter and GIL. Mind pickling cost and chunk size. Subinterpreters or a free-threaded build are alternatives.

Answer that loses points

“Use ThreadPoolExecutor with 8 workers.”

How would you speed up 500 HTTP calls?
Short answer

Use asyncio or a thread pool, because the calls are I/O-bound.

If they dig deeper

Latency dominates, so the waits overlap. Cap concurrency with a semaphore. Processes add heavy overhead for little gain.

Answer that loses points

“Use multiprocessing because it is faster.”

Is asyncio parallel?
Short answer

No. It runs on one thread and is concurrent only.

If they dig deeper

The event loop runs one callback at a time and tasks interleave at await. Parallelism appears only if you offload work to threads or processes.

Answer that loses points

“asyncio runs tasks on multiple cores.”

Can more threads make things slower?
Short answer

Yes, from contention and context switching.

If they dig deeper

Once cores or I/O are saturated, extra threads add switching cost, stack memory and lock contention. With the GIL, an I/O thread can also queue behind a CPU-bound thread each time the GIL changes hands.

Answer that loses points

“More threads is always faster.”

Cheat-sheet

Commands you will actually type.

import os; print(os.process_cpu_count())

Cores this process may use (3.13+). `os.cpu_count()` is the machine total.

from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor() as ex:
    list(ex.map(fetch, urls))

Threads for waiting-heavy work. Default workers: `min(32, cpus + 4)` on 3.13+.

from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor() as ex:
    list(ex.map(crunch, jobs, chunksize=50))

Processes for CPU-heavy work.

import asyncio
await asyncio.gather(*(fetch(u) for u in urls))

Many I/O waits on one thread. Concurrent, not parallel.

import sys; sys._is_gil_enabled()

3.13+: is the GIL on right now? Tells you if threads can run bytecode in parallel.

python -VV

Mentions “free-threading build” when you run one.

import time
t = time.perf_counter()
work()
print(time.perf_counter() - t)

Measure wall-clock before and after. Never assume a speed-up.

Sources