Why adding more workers can make a job slower
Increasing workers from 16 to 64 made the nightly job 86 seconds slower.
Read Lesson F01.1 first: it defines record, job, worker, wall time, throughput, latency and floor. Nothing to install.
The problem: more workers, slower execution
On 16 single-core workers the nightly validation job took 175 seconds. On 64 it took 261 seconds: 86 seconds slower, with the same code and input data. Why did adding more workers make the job slower?
Breaking down the job’s runtime
Let’s rebuild the job’s runtime on 16 and on 64 workers, one part at a time. Each part comes with the assumption it makes, so you can see exactly where the ideal stops.
Serial work
10 sPaid onceThe coordinator spends 5 seconds reading the input manifest before processing begins, and another 5 seconds writing the final summary. This work cannot be divided among workers.
Assumes: it stays at 10 s however large the input grows. §3 shows what changes if it grows too.
At 16 workers and at 64: 10.0 s.
How Runtime Changes with Worker Count
| Workers | Serial + Fixed | Worker Overhead | Parallel Work | Total Runtime |
|---|---|---|---|---|
| 1 | 50 s | 3 s | 1,232.4 s | 1,285.4 s |
| 8 | 50 s | 24 s | 154.1 s | 228.1 s |
| 16 | 50 s | 48 s | 77.0 s | 175.0 s |
Notice what happens as we add workers:
- Parallel work decreases: More workers share the same amount of processing.
- Per-worker overhead increases: Every additional worker adds 3 seconds of overhead.
- Serial work and fixed overhead remain unchanged: Adding workers does not reduce these costs.
An important observation: Running the job with one worker on the cluster takes 1,285.4 seconds, while running it as a standalone process without the cluster takes only 1,242.4 seconds. The difference is 43 seconds of additional overhead. With only one worker, the cluster provides no parallel-processing benefit, but we still pay the cost of setting it up and managing that worker.
Our simulator incurs 3 seconds of overhead per worker, including launch, work assignment, and result collection.
If we increase the cluster to 32 workers, how much total per-worker overhead will the job incur?
Understanding the limits of parallelism
Adding more workers can reduce processing time, but some parts of a job cannot be executed in parallel.
Let’s modify our nightly validation job to understand why. Suppose all order events arrive in a single compressed archive. Before workers can process the records, the coordinator must unpack the archive. In our example:
- Serial work: Unpacking the archive takes 150 seconds, followed by 5 seconds to generate the final summary. Total: 155 seconds.
- Parallel work: Validating all records takes 1,232.4 seconds on a single processor.
Without cluster overhead, the total time on one processor would be: 155 + 1,232.4 = 1,387.4 seconds
Now, imagine we could add unlimited workers without any startup or coordination overhead. As we add workers, the parallel processing time gets smaller and approaches zero. However, the 155 seconds of serial work remain unchanged, regardless of how many workers we add. Therefore, even with unlimited workers, the job cannot finish in less than 155 seconds. The maximum possible speedup is:
Maximum speedup = Single-process time ÷ Serial processing time = 1,387.4 ÷ 155 ≈ 8.95×
This is an example of Amdahl’s Law, which explains how the portion of a program that cannot be parallelised limits its maximum speedup. The serial fraction is the proportion of total processing time spent on work that cannot be divided among workers.
In our example: Serial fraction (s) = 155 ÷ 1,387.4 ≈ 0.112 (11.2%)
According to Amdahl’s Law, the theoretical maximum speedup with unlimited workers is:
Even with unlimited computing resources, the job cannot exceed this theoretical speedup unless we also reduce the serial work.
Our job requires 155 seconds of serial work and 1,232.4 seconds of parallel work.
If we could use unlimited workers with no overhead, what would be the maximum possible speedup?
What Happens When the Dataset Gets Larger?
Now suppose the number of order events triples, but the serial work remains fixed at 155 seconds. The parallel work increases from 1,232.4 seconds to: 1,232.4 × 3 = 3,697.2 seconds
The new single-process runtime becomes: 155 + 3,697.2 = 3,852.2 seconds
If the serial work stays at 155 seconds while the parallel work triples, what happens to the theoretical maximum speedup?
Why more workers made the job slower
Let’s return to our nightly validation job. With 16 workers, the job completes in 175 seconds. The team now increases the cluster to 64 workers, expecting a significant improvement. But will four times as many workers make the job four times faster?
How long will the job take with 64 workers?
Finding the right number of workers
We’ve seen that adding workers has two opposite effects:
- Parallel processing time decreases because more workers share the workload.
- Worker overhead increases because each additional worker requires coordination.
This means there is a point where these two costs are balanced, giving us the shortest possible runtime for our model. Let’s find that point.
How Runtime Changes with Worker Count
Our simulator uses the following costs:
- Serial work + fixed overhead: 50 seconds
- Total parallel work: 1,232.4 seconds
- Overhead per worker: 3 seconds
For any number of workers, the runtime is: Total runtime = 50 + (3 × workers) + (1,232.4 ÷ workers)
Let’s compare different worker counts.
| Workers | Worker Overhead | Parallel Work | Total Runtime | Billed Core-Seconds |
|---|---|---|---|---|
| 1 | 3.0 s | 1,232.4 s | 1,285.4 s | 1,285 |
| 2 | 6.0 s | 616.2 s | 672.2 s | 1,344 |
| 4 | 12.0 s | 308.1 s | 370.1 s | 1,480 |
| 8 | 24.0 s | 154.1 s | 228.1 s | 1,824 |
| 16 | 48.0 s | 77.0 s | 175.0 s | 2,800 |
| 20 | 60.0 s | 61.6 s | 171.6 s | 3,432 |
| 32 | 96.0 s | 38.5 s | 184.5 s | 5,904 |
| 64 | 192.0 s | 19.3 s | 261.3 s | 16,720 |
| 128 | 384.0 s | 9.6 s | 443.6 s | 56,784 |
Core-seconds are calculated using the model’s assumption that every worker is billed for the entire job duration. Values are rounded.
Notice the pattern:
- From 1 to 20 workers, the total runtime decreases as parallel processing becomes faster.
- At 20 workers, the job reaches its shortest runtime of 171.6 seconds.
- Beyond 20 workers, the increasing overhead outweighs the savings from parallel processing, so the job becomes slower.
If we plot worker count against runtime, the curve falls initially, reaches a minimum, and then rises again. This produces a U-shaped curve.
Calculating the Optimal Worker Count
For this simplified model, we can estimate the worker count that minimises runtime using:
Substituting our values:
√(1,232.4 ÷ 3) ≈ 20.3 workers
Since we need a whole number of workers, 20 workers is the best choice for minimising runtime in this example. This formula assumes evenly distributed work and no additional resource bottlenecks. Real systems may require measurements to find their optimal configuration.
The operations team asks you to choose the worker count that completes the nightly job in the shortest time.
Which would you recommend?
What About Resource Cost?
Let’s look at the last column of the table. A core-second represents one CPU core allocated for one second. In our billing model, every worker is charged for the entire job runtime, whether it is actively processing or waiting.
Compare two configurations:
| Metric | 8 Workers | 20 Workers |
|---|---|---|
| Total runtime | 228.1 s | 171.6 s |
| Billed core-seconds | 1,824 | 3,432 |
Using 20 workers makes the job 56.5 seconds faster, but requires substantially more billing on compute resources.
Which configuration is better depends on the requirement:
- If finishing as quickly as possible matters most, choose 20 workers.
- If minimising compute cost matters more, fewer workers may be preferable, provided the job meets its deadline.
We’ll explore these cost and performance tradeoffs in F01.3.
Does the Optimal Worker Count Always Stay the Same?
No. The optimal number of workers changes with the workload and overhead:
- More parallel work: A larger workload can benefit from additional workers.
- Higher per-worker overhead: Expensive worker startup or coordination makes fewer workers more attractive.
- Shared-resource bottlenecks: Disk, network, or other resource limits may prevent additional workers from improving performance.
The goal is not to use the maximum number of workers. It is to find the worker count that provides the best balance between processing time, coordination overhead, and resource cost.
What this shows and its limitations
Our simulation demonstrates three important lessons:
- More workers introduce additional overhead: Parallel processing becomes faster as workers are added, but the cost of managing those workers also increases. Beyond a certain point, adding workers can slow the job down.
- Serial work limits maximum speedup: Tasks that cannot be parallelised take the same amount of time regardless of how many workers are available.
- Distributed execution has a cost: Running a job on a cluster with just one worker can be slower than running it as a standalone process because of additional setup and coordination overhead.
Limitations of the Model
Our simulation makes a few simplifying assumptions:
- Sequential worker coordination: The coordinator starts workers and collects results one at a time. Real distributed engines can perform some of these operations concurrently.
- Equal workload distribution: Every worker receives the same amount of work. In F02, we’ll explore how uneven data distribution affects performance.
- No failures or retries: The model assumes every worker completes successfully. We’ll study failure recovery in F04.
- No additional resource bottlenecks: The optimal-worker formula assumes processing time decreases as work is divided. Shared disks or network limits can change the result, as we saw in F01.1.
The model helps explain why adding workers eventually stops improving performance. In real systems, use actual execution metrics to identify the best worker count.
Why one slow worker can delay the entire job
Even when work is distributed evenly, not every worker finishes at the same time. A worker might run slower because of a busy disk, limited CPU resources, or contention with another process. In distributed processing, some stages require all workers to finish before the next stage can begin. This synchronisation point is called a barrier. The slowest worker determines when the stage finishes. Let’s see how this affects our nightly validation job.
With 16 workers, each worker normally completes its share of processing in 77 seconds. Now suppose one worker takes 30% longer than the others.
How much will the total job runtime increase?
How Can We Reduce This Problem?
One possible approach is to divide the workload into smaller tasks, allowing work to be distributed more flexibly instead of assigning one large portion to each worker. We’ll explore workload distribution and uneven partitions in F02. Another issue worth investigating is hidden serial work. A job may contain an unexpected step that runs on only one process. Such a step can limit speedup even when the rest of the work is highly parallel.
In distributed systems, overall performance can be limited by the slowest worker. Always investigate slow tasks and synchronisation points when a parallel job takes longer than expected.
How to recognise parallelism bottlenecks
How can you tell when adding workers is no longer helping? In our simulator, look for three warning signs:
- Worker overhead grows faster than processing time decreases: Additional workers cost more time to manage than they save through parallel processing.
- Total runtime increases despite faster parallel processing: Each worker finishes its share sooner, but the complete job takes longer.
- Serial work limits speedup: A significant portion of runtime remains unchanged, regardless of how many workers are added.
In real distributed systems, similar symptoms include:
- A job becomes slower after increasing the cluster size.
- Worker startup or task scheduling takes a significant portion of total runtime.
- Processing speed stops improving despite adding more workers.
- Most workers finish early but must wait for one slow task.
More workers are useful only when they reduce total execution time. Examine both processing time and coordination overhead to understand why scaling has stopped helping.
What to check before adding workers
Before increasing the number of workers, ask yourself four questions:
Identify operations that cannot be divided among workers. These determine the theoretical speedup limit.
Separate costs paid once per job from those that increase with every worker.
Use the runtime model as a starting estimate, then compare different worker counts through measurements.
Compare the reduction in runtime against the increase in resource consumption, measured in core-seconds.
For our simplified model, the worker count that minimises runtime is approximately:
However, this formula assumes evenly divided work and no additional resource limits. Real systems may behave differently.
Check your understanding: what if worker overhead doubles?
Let’s return to the nightly validation job. Previously, each worker introduced 3 seconds of overhead. Now suppose worker startup, coordination, and result collection become more expensive, increasing the overhead to 6 seconds per worker. The total parallel work remains unchanged at 1,232.4 seconds.
If per-worker overhead doubles from 3 to 6 seconds:
- What happens to the optimal worker count?
- How long will the job take with 16 workers?
Choose the correct answer:
Try It Yourself
When the F01 workbench is available, run:
python -m sim sweep --job nightly_validate --cluster lab_cluster
The simulator compares worker counts and reports their execution times. Observe the worker count with the shortest runtime and identify the two competing costs: parallel processing time and per-worker overhead.
The optimal worker count depends on both the workload and the cost of managing workers. When per-worker overhead increases, using fewer workers may produce better performance.
Summary
Adding more workers reduces the amount of parallel work each worker must perform, but it also introduces additional coordination overhead.
A distributed job’s total runtime depends on four components:
- Serial work: Operations that cannot be divided among workers.
- Fixed overhead: Setup and coordination costs paid once per job.
- Parallel work: Processing time that decreases as work is distributed among more workers.
- Per-worker overhead: Costs that increase with the number of workers.
In our nightly validation example:
- 16 workers completed the job in 175 seconds.
- 64 workers increased the runtime to 261.3 seconds, approximately 86 seconds slower.
- 20 workers provided the shortest runtime of 171.6 seconds in the original model.
We also learned that Amdahl’s Law limits the maximum speedup when part of a job cannot be parallelised. Additionally, a single slow worker can delay an entire processing stage when all workers must finish before execution continues.
Where This Matters
In production systems such as Apache Spark, similar performance problems can arise from driver-side processing, task scheduling overhead, uneven workloads, and workers waiting at stage boundaries.
We’ll explore Spark’s execution model and scheduling behavior in S01.
The fastest distributed job isn’t necessarily the one with the most workers. Identify serial work, measure coordination overhead, and find the worker count that delivers the best performance for your requirements.
Sources. Capture: reference model sim 0.1, Python 3.13.16, 2026-10-13; profiles nightly_validate, lab_cluster and the page-only nightly_validate_unpack (150 s unpack step) and lab_cluster_c1x2 (6 s per worker). Amdahl, G. M. (1967), “Validity of the single processor approach to achieving large scale computing capabilities”, AFIPS Spring Joint Computer Conference, vol. 30, pp. 483–485. Gustafson, J. L. (1988), “Reevaluating Amdahl’s law”, Communications of the ACM 31(5), pp. 532–533.