In this article
  1. Before you start
  2. Why interviewers ask this
  3. Parallel processing from first principles
  4. Worked example
  5. Trade-offs and edge cases
  6. How to answer it in an interview
  7. Common mistakes
  8. Follow-up questions
  9. Sources

Before you start

Why interviewers ask this

The question separates candidates who say "use more cores" from candidates who can say what limits the gain and which Python tool fits the job. Saying "just add threads" is the answer that costs the point, because in the standard Python interpreter threads do not speed up computing, and the follow-up arrives within seconds.

In a coding round it rarely comes as a definition. It comes as a task: process a million records faster, or explain why a data-loading job got no faster after adding workers. It also underlies design questions such as batch versus streaming inference, where work is split across machines.

A weak answer names a tool. A strong answer names the bottleneck first, the speedup limit second and the tool third.

Parallel processing from first principles

In plain words

Parallel processing
Parallel processing is the execution of several parts of one job at the same time on separate processor cores, so that the job finishes sooner than it would on a single core.
Process
A process is a running program with its own private memory, which the operating system schedules on a core independently of other processes.
Thread
A thread is a path of execution inside a process that shares the process's memory with its sibling threads.
Global interpreter lock
The global interpreter lock (GIL) is the mechanism in the standard CPython interpreter that lets only one thread run Python bytecode at a time. Bytecode is the low-level instruction list that Python translates your code into before running it.

Picture a kitchen that must make eighty sandwiches. One cook makes them one by one. Four cooks each take twenty and finish in about a quarter of the time. Someone still has to buy the bread first, and that errand does not get faster with more cooks.

The analogy breaks in one place. Cooks share one kitchen, while Python processes do not share memory by default, so data has to be copied between them.

In one precise sentence, parallel processing splits a job into independent tasks, runs them on separate cores together and combines the results, and its speedup is capped by the part that cannot be split.

Step 1: Split the job into independent tasks

The first step is to cut the work into tasks that do not need each other's results while they run. The multiprocessing documentation calls this data parallelism: a pool distributes the input data across processes so one function runs over many inputs.

The question to ask is what depends on what. Summing squares over a range is easy to split, because each block of numbers is independent. In the worked example a range of four million numbers is cut into four blocks of one million each1. Hashing a value a million times, where each round's input is the previous round's output, cannot be split at all, because round two cannot start until round one ends. A running total also needs the previous value, yet a parallel scan (Hillis and Steele, 1986; Blelloch, 1990) computes it in two passes, so "needs the previous value" often means "needs a different algorithm".

The output is a list of tasks plus the work that must stay in order, the subject of the next step.

Step 2: The serial part caps the speedup

Some work cannot be split, and that serial part sets the ceiling on the speedup, which is how many times faster the parallel run is than the single-core run. Amdahl's law is the formula for the ceiling: with a fraction p of the work parallel and n workers, the speedup is 1 divided by the sum of (1 minus p) and p divided by n.

Take a job that is 90% parallel. On eight cores the speedup is 4.71, not 8, and it can never pass 10 however many cores are added; it takes 9 cores just to reach 51. The idea is from Gene Amdahl's 1967 paper, which argued that data-management housekeeping accounted for 40% of executed instructions in production runs and limited what many processors could add. The paper itself contains no equations; the formula is the standard later statement of its argument.

Input: the share of work that is parallel. Output: a ceiling you can compute before writing code.

Step 3: A pool hands the tasks to workers

A pool is a fixed set of workers that takes tasks from a queue, so you do not start one process per task. The multiprocessing documentation's example is Pool(5) with p.map(f, [1, 2, 3]), which returns [1, 4, 9].

In the newer interface, concurrent.futures, a ProcessPoolExecutor defaults to os.process_cpu_count() workers, and a ThreadPoolExecutor defaults to min(32, (os.process_cpu_count() or 1) + 4) since Python 3.13 (concurrent.futures documentation, Python 3.15.1). The thread default is larger because threads often wait rather than compute.

In the run, executor.map over the four blocks returned the partial results in input order1. Choose the worker count from the number of cores, then measure; more workers than cores adds switching overhead and no extra speed for computing.

Step 4: The cores run the tasks together

Workers only help if they can run at the same time on different cores, and in standard Python that depends on whether you use threads or processes. The glossary says the GIL lets only one thread execute Python bytecode at a time (Python glossary). The threading documentation therefore advises multiprocessing or ProcessPoolExecutor for multi-core computing, and says threading is still appropriate for several tasks that wait on input and output (threading documentation).

Processes side-step the GIL, as the multiprocessing page puts it, "by using subprocesses instead of threads". The price is that only picklable objects, meaning objects Python can convert to bytes, can be sent to a worker, so a lambda (a one-line unnamed function) or a function defined in the interactive prompt does not work.

Since Python 3.13 a separate free-threaded build can run threads on several cores. Its documentation reports an average overhead from about 1% on macOS aarch64 (the 64-bit Arm chips in recent Macs) to 8% on x86-64 Linux (x86-64 is the 64-bit Intel and AMD chip design), and says some extension modules re-enable the GIL. An extension module is an add-on library written in a compiled language such as C (free-threading documentation).

Step 5: Combine the results in order

The last step gathers the partial results into one answer, and the answer must match what the single-core run would give. In the worked example the four partial sums added up to 21,333,325,333,334,000,000, the same total as the serial run1.

The safe pattern is for each worker to return a value and for the parent to combine them, with no shared variable. Shared state breaks easily. If two workers read a counter that holds 0 and both write back 1, the counter ends at 1 after two increments, and one update is lost; that interleaving is written out by hand in the run1. The multiprocessing page notes that += on a shared value is not atomic (it is not one indivisible step, so another worker can slip in between its read and its write) and recommends holding the value's lock around it.

Output: one combined result and a check that it equals the serial one.

A parallel job splits, meets a limit, runs together and combinesFlow of five stages: one job is split into independent tasks, the serial part sets a ceiling on speedup, a pool hands tasks to workers, cores run them together, and the results are combined.One jobsplit it upSerial partsets the ceilingWorker poolhands outtasksCores runtogethersame timeCombineresultsin orderDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/parallel-processing.html

Parallel speed comes from splitting well, and the part that cannot be split sets the ceiling.CC BY 4.0 — source: What is parallel processing?
Step-by-step description
  1. Cut the job into independent tasks.
  2. The serial part caps the speedup.
  3. A pool hands each task to a worker.
  4. Workers run on separate cores at the same time.
  5. Gather the partial results in order.
  • A job is split into independent tasks.
  • The part that cannot be split limits the speedup.
  • A pool of workers runs the tasks on separate cores at the same time, and the results are combined.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Worked example

With 90% of the work parallel, 8 cores give a 4.71 times speedup and 16 cores give 6.4 times, against a ceiling of 10 times, so doubling cores gains far less than double.
Parallel share2 cores4 cores8 cores16 coresCeiling
50%1.331.61.781.882.0
90%1.823.084.716.410.0
95%1.93.485.939.1420.0
99%1.983.887.4813.91100.0

Each cell is 1 divided by (1 minus the share plus the share divided by the core count), computed in the run1. A reader can redo any cell with a calculator, and the two numbers to remember are the 90% row at eight cores and its ceiling.

The next cost is uneven work. Take eight illustrative tasks that take 5, 3, 8, 2, 7, 4, 6 and 1 time units, 36 in all, and three workers, where each task goes to whichever worker is free first. The three workers finish at 12, 10 and 14, so the job takes 14 units, a speedup of 2.571 instead of the ideal 3, because one worker got the long tasks1.

Speedup flattens as cores grow when part of the job is serialLine chart of speedup on 2, 4, 8 and 16 cores for jobs that are 50, 90 and 99 percent parallel: with 90 percent parallel work, 8 cores give 4.71 times and 16 cores give 6.4 times.Speedup (times) (x)148121624816Number of cores50% parallel90% parallel99% parallelDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/parallel-processing.html
Doubling the cores of a 90 percent parallel job from 8 to 16 gains about a third, not double.CC BY 4.0 — source: What is parallel processing?
Text description of the diagram
  • With 90 percent parallel work, 8 cores give a speedup of 4.71 and 16 cores give 6.4.
  • With 50 percent parallel work the speedup never reaches 2.
Seven facts about parallel work in Python that are safe to quote, each from the Python 3.15.1 documentation or from the run.
FactValue
Speedup, 8 cores, 90% parallel4.71 times
Ceiling, 90% parallel10 times
Cores to reach 5 times at 90% parallel9
Default ProcessPoolExecutor workersos.process_cpu_count()
Default ThreadPoolExecutor workers (3.13 and later)min(32, (os.process_cpu_count() or 1) + 4)
Default start method on macOS and Windowsspawn
Free-threaded build, average overheadabout 1% to 8%

Nothing here was timed. The run checks results and arithmetic only, so no machine-speed claim is made.

Trade-offs and edge cases

Parallel processing trades simplicity and memory for speed, and the trade is worth it only when the task is big enough and independent enough.

  • Small tasks. Starting workers and copying data costs time, so a task that takes less than that cost runs slower in parallel.
  • Uneven tasks. As in the scheduling example, one long task keeps the job waiting; split the work finer so a pool can balance it.
  • Waiting versus computing. Downloading many pages is waiting, so threads are enough. Crunching numbers is computing, so use processes. The table below summarises the choice.
  • Memory. Each process has its own memory, so a large input copied to four workers uses about four times the memory unless it is shared deliberately.
  • Start method. On macOS and Windows the default start method is spawn, and since Python 3.14 Linux defaults to forkserver. A start method is how a new worker process is created: spawn launches a fresh Python that re-imports your file, and forkserver asks a small helper process to create workers. Because the file is re-imported, the entry point (the code that starts the program) needs the if __name__ == '__main__' guard, which means "run this only when the file is started directly"; without it workers fail to import the module.
  • Libraries that already do it. Many numerical libraries run compiled code outside the GIL, and a graphics processing unit (GPU) does the same job in hardware, so check before writing your own.
Pick threads for waiting and processes for computing in PythonTable of three Python options, a thread pool, a process pool and a free-threaded build, with the job each fits and the main thing to watch for: waiting on input and output, heavy computing, and library support.ToolFitsWatch forThread poolWaiting on I/OOne thread runsPythonProcess poolHeavycomputingData must bepicklableFree-threaded buildThreads on allcoresLibrary supportDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/parallel-processing.html
Match the tool to the bottleneck: threads for waiting, processes for computing.CC BY 4.0 — source: What is parallel processing?
Text description of the diagram
  • A thread pool suits tasks that mostly wait on input and output.
  • A process pool suits heavy computing, and only picklable objects can be sent to it.

How to answer it in an interview

Interviewer asks

What is parallel processing, and when does it help?

A strong answer

Parallel processing splits a job into independent tasks and runs them on separate cores at the same time. It helps when the work is heavy and splittable. The ceiling is Amdahl's law: if ninety percent of the work is parallel, eight cores give about 4.7 times, and ten times is the limit. In Python, threads do not speed up computing because of the global interpreter lock, so I would use a process pool for computing and threads for waiting on input and output. I would return results from workers, not share a counter.

The spoken structure is: define it, state the Amdahl ceiling with one number, name processes for computing and threads for waiting, then say how you avoid shared state.

Common mistakes

  • "More cores always means proportionally faster" (wrong). The serial part caps it (right: say 90% parallel gives at most 10 times, and 4.71 on eight cores).
  • "Threads speed up CPU-heavy Python" (wrong). In standard CPython one thread runs Python bytecode at a time (right: use processes for computing, threads for waiting).
  • "Concurrency and parallelism are the same" (wrong). Concurrency is managing several tasks that overlap in time, and parallelism is running them at the same instant on separate cores (right: parallelism needs several cores).
  • "Workers can all increment one shared counter" (wrong). Two workers can both write 1 and lose an update (right: return values and combine them, or hold a lock).
  • "Any function can be sent to a process pool" (wrong). Only picklable objects can be sent (right: use a named top-level function, not a lambda).

Follow-up questions

What is the difference between parallelism and concurrency?

Concurrency is structuring a program so that several tasks make progress in overlapping time periods, even on one core, while parallelism is running tasks at the same instant on different cores. A single-core machine can be concurrent but not parallel. The concurrency interview questions page goes deeper.

Why does Python threading not speed up CPU-heavy code?

Because in standard CPython the global interpreter lock lets one thread run Python bytecode at a time, so threads take turns on one core. They still help when tasks wait on input and output, since a waiting thread releases the turn. A free-threaded build removes the limit, at a small overhead.

What is Amdahl's law?

It says the speedup from n workers on a job with parallel share p is 1 divided by the sum of (1 minus p) and p divided by n, so the serial share sets a ceiling of 1 divided by (1 minus p). At 90% parallel the ceiling is 10 times.

What is the difference between multithreading and multiprocessing?

Threads share one process's memory and are cheap to start, while processes have separate memory and need data copied between them. In Python, multiprocessing gives real multi-core speed for computing and multithreading suits waiting. A defaultdict built with a lambda cannot be sent to another process, which is one reason named factories are safer.

When should you not parallelise a task?

When the task is small, when each step needs the previous result, or when the bottleneck is a shared resource such as a disk, a network link or a lock. Measure the single-core run first. For work spread across machines, see system design.

Key takeaways
  • Parallel processing runs independent tasks on separate cores at the same time and combines their results.
  • Amdahl's law caps the speedup: at 90% parallel, 8 cores give 4.71 times and the ceiling is 10 times.
  • In standard Python use processes for computing and threads for waiting on input and output.
  • Return values from workers instead of sharing a variable, or protect it with a lock.
  • Split the work finely enough that a pool can balance it, and check the result equals the single-core one.

Sources

  1. multiprocessing: Process-based parallelism (Python 3.15.1 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  2. concurrent.futures: Launching parallel tasks (Python 3.15.1 documentation) — Python Software Foundation (page footer: last updated 2026-10-24), 24 Oct 2026
  3. threading: Thread-based parallelism (Python 3.15.1 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  4. Python support for free threading (Python 3.15.1 documentation) — Python Software Foundation (page last updated 2026-10-24), 24 Oct 2026
  5. Glossary: global interpreter lock (Python 3.15.1 documentation) — Python Software Foundation (read 2026-10-24), 24 Oct 2026
  6. Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities (Amdahl, AFIPS Conference Proceedings vol. 30; 2007 reprint) — AFIPS Press (reprinted in the IEEE SSCS News, Summer 2007), 18 Apr 1967