Four loads of laundry take less time when four washing machines run at once. Parallelism works the same way: multiple computations physically execute at the same time. It differs from serial execution, which handles work one step at a time. section 2.6
Parallel execution needs both independent tasks and multiple processing resources. Independence means tasks can run in different orders without changing the result. If one task needs another’s output, it must wait; synchronization can limit the benefit. A single processing resource cannot provide true parallelism. section 2.8 section 2.9
Concurrency means tasks make progress over overlapping periods; it does not guarantee that they execute simultaneously. One cook can alternate between chopping and stirring, while two cooks can do both at once. Likewise, multitasking can switch rapidly between threads on one core without making their execution physically parallel. section 2.13 section 6.8
Searching for a password
A sequential brute-force search checks number combinations one by one. Split the range into chunks, then give each chunk to a worker process through Python’s Pool. In one run, the sequential search took about 64.6 seconds; the chunked search took about 17.2 seconds. The gain comes from searching independent ranges on multiple cores, not merely from creating tasks. section 2.10 section 5.5
More processors do not promise proportionally shorter runs. Dividing work and collecting results can remain sequential: Amdahl’s law limits speedup when the problem size stays fixed. Gustafson’s law instead considers how much more work the processors can finish in the same time. Check the bottleneck, too: extra cores may help CPU-bound work but not work spent waiting for I/O. section 2.11 section 2.12 section 6.4





































