Parallel Computing Fundamentals
Parallel Computing Fundamentals
Begin
13 pages · ~26 min
Interactive digital-human course

Parallel Computing Fundamentals

This training introduces the fundamentals of parallel computing, helping learners understand how to design and implement parallel algorithms for improved performance.

My workspace26 minFree to watch

What you’ll learn

  1. 01Introduction to Parallel Computing: Why Do Many Things at Once?Welcome. I'm glad you're here. Today we're going to explore a fundamental shift that shapes nearly every piece of technology you use: parallel computing. The core idea is deceptively simple—why do just one thing at a time when you can do many things at once? By performing many calculations simultaneously, we can solve problems dramatically faster. For decades, the 'free lunch' of performance came from making a single processor run at higher and higher clock speeds. But around 2005, that free lunch ended. As single-core frequencies increased, they hit a power and heat limit—pushing chips faster would literally melt them. The industry responded by placing multiple, simpler processing cores on a single chip, running them side-by-side at lower, more efficient speeds. If you think of the old approach as a single, increasingly frantic chef, the new approach is like adding more prep stations in a kitchen, or more lanes on a highway to handle traffic. This shift made parallel processing essential. Today, whether you're browsing the web, playing a video game, using a smartphone app, or encoding a video, you are relying on multicore processors that juggle countless tasks across many cores simultaneously. So, where does all that parallel work begin? It starts by breaking a big problem into smaller, manageable tasks. We'll examine that next: 'Tasks and Decomposition: Breaking Work into Manageable Pieces.'Introduction to Parallel Computing: Why Do Many Things at Once?arxiv.orgarxiv.orgdoi.org+22 min
  2. 02Tasks and Decomposition: Breaking Work into Manageable PiecesNow let's talk about the very first action: breaking our work into manageable pieces. Parallelism isn't magic; it starts with identifying tasks that can run simultaneously without having to wait for data from each other. Think of it this way: we have two fundamental approaches. With data parallelism, the same operation is applied across different chunks of data, like resizing every image in a folder at once. With task parallelism, we run completely different tasks on the same or different data, like processing the text, charts, and appendices of a report all at the same time. The trick is finding the right granularity. If we make the tasks too big and coarse, some processors might end up sitting idle, waiting for the heavy ones to finish. But if we slice the work into thousands of tiny fine-grained pieces, we improve balance, but add massive management overhead. The real pitfall here is hidden data dependencies. You might think two tasks are independent, but they secretly touch the same variable, blocking true parallel execution. Understanding this decomposition is the foundation. Next, we'll see how these tasks are actually packaged and run, looking at threads, processes, and how work actually runs.Tasks and Decomposition: Breaking Work into Manageable Piecesintel.comkindatechnical.compatterns.eecs.berkeley.edu+22 min
  3. 03Threads, Processes, and How Work Actually RunsNow that we have a high-level picture, let's get more precise about how our work actually runs inside a computer. The operating system sees two main actors here: processes and threads. Think of a process as an independent program with its own private memory space, like a house with its own walls. Threads, on the other hand, live inside that house. They share the same rooms and furniture, meaning they share the same memory within the process. So you get fast communication, but that also introduces the challenge of coordination. The operating system's scheduler then acts like a time manager, rapidly switching between all these threads on the available physical cores, a trick called time-slicing, to create the illusion that everything is happening at once. It also uses core affinity to keep a thread running on the same core for better performance. This is very different from how a GPU works. A GPU contains thousands of simpler, smaller cores designed for massive data parallelism. For example, applying a filter to every pixel of an image at the same time, which is ideal for graphics and machine learning. A key distinction to remember is the difference between hardware concurrency, which is the actual number of physical cores you have, and software parallelism, which is how we write our code to express multiple tasks that can run at the same time, regardless of the hardware underneath. These threads and processes don't just run independently; they often need to talk to each other. That's the world of inter-process communication, which brings us to our next slide: the two fundamental communication models, Shared Memory and Message Passing.Threads, Processes, and How Work Actually Runsadhdecode.comida.liu.sescalemind.dev+22 min
  4. 04Shared Memory and Message Passing: Two Communication ModelsNow, when tasks need to work together, they need a way to talk. There are two dominant communication models. The first is Shared Memory. Picture a giant shared whiteboard: multiple threads can read and write to common variables directly. It's extremely fast because there’s no copying of data. But chaos can happen fast too. If two threads try to write 'three' and 'seven' on the same spot at the same time, you get a corrupted mess. To prevent this, we use locks—think of a bouncer letting only one person write at a time. This model shines inside a single multicore machine using your CPU’s cache. The second model is Message Passing. Instead of a shared board, processes stay in their own isolated rooms with their own whiteboards. To communicate, they send a letter—an explicit message. This isolation is inherently safer, avoiding those messy overwrites. Because there is no shared memory, it scales beautifully across different machines in a cluster, making it the backbone of distributed systems. The trade-off is speed; packaging and copying those letters adds overhead. So, when do you pick which? Choose Shared Memory for raw speed and tight synchronization on one machine. Choose Message Passing for safer isolation and scalability across a network. Making this choice well sets you up for the next big challenge: ensuring those tasks stay safe and orderly, no matter how they talk.Shared Memory and Message Passing: Two Communication Modelsadhdecode.comida.liu.sescalemind.dev+22 min
  5. 05Synchronization and Coordination: Keeping Tasks Safe and OrderlyLet’s talk about how we keep concurrent tasks safe and orderly. When multiple tasks access shared data at the same time, we can hit what’s called a race condition. Think of two threads trying to increment the same counter simultaneously—the results become unpredictable, and the output changes every run. To prevent this, we use mutual exclusion, or a mutex. A mutex acts like a lock on a door; only one thread can hold the lock and enter the critical section at a time, protecting the data. Sometimes, all tasks need to reach the same point before moving on. That’s where barrier synchronization comes in—a rendezvous point that holds everyone until the last task arrives. But here’s the coordination trade-off: too much locking creates queues and kills performance, while too little risks incorrect results. Finding the right balance is key. Next, we’ll explore the fundamental limits that cap our parallel speedup, including Amdahl’s Law, communication costs, and load imbalance.Synchronization and Coordination: Keeping Tasks Safe and Orderlyadhdecode.comida.liu.sescalemind.dev+21 min
  6. 06Fundamental Limits: Amdahl’s Law, Communication, and Load ImbalanceMoving into the hard reality of parallel design, let's look at the fundamental limits. Amdahl's Law gives us a mathematical ceiling. The formula is speedup equals one divided by the serial fraction plus the parallel fraction over N. Think of it this way: even if you had a million cores, your speedup is capped by the part of the code you can't parallelize. For instance, if just five percent of your program must remain serial, your absolute maximum speedup is twenty times, no matter what. There are other practical walls too. Communication costs matter because sending data between workers incurs latency, and load imbalance kills efficiency. If you have a team of workers but one takes twice as long on their chunk, everyone else sits idle waiting for the slowest one to finish. These are not just math problems; they are real constraints you will hit in any system, whether it uses shared memory or message passing. Next, we will explore how to structure our work to navigate these limits by looking at Common Parallel Patterns: MapReduce, Fork-Join, and Pipelines.Fundamental Limits: Amdahl’s Law, Communication, and Load Imbalanceadhdecode.comida.liu.sescalemind.dev+22 min
  7. 07Common Parallel Patterns: MapReduce, Fork-Join, and PipelinesSo how do we actually structure work to run in parallel? Let's look at three essential patterns you'll encounter again and again: MapReduce, Fork-Join, and Pipeline. Think of MapReduce as distributing the same operation across a massive dataset. You map your function onto independent chunks of data, and then reduce—or combine—all the partial results into a final answer. This is perfect for when you have zero dependencies between those data chunks. Next, Fork-Join is a classic divide-and-conquer approach. You recursively split a problem, fork parallel sub-tasks to solve each piece, and then join the results by merging them back together. This pattern shines in recursive algorithms like merge sort. Finally, the Pipeline pattern works like an assembly line. You set up a chain of stages, where each stage's output feeds directly into the next stage concurrently. While one stage processes a data item, the previous stage is already working on the next one. This is ideal for streaming or multi-step workflows. So here's a quick way to remember: use MapReduce for independent operations on big data, Fork-Join for recursive splitting, and Pipelines for continuous streaming tasks. Now that we've seen these patterns for organizing parallel work, we need to talk about what can go wrong. Let's move on to the pitfalls that break parallel programs, including deadlock, false sharing, and data races.Common Parallel Patterns: MapReduce, Fork-Join, and Pipelinesadhdecode.comida.liu.sescalemind.dev+21 min
  8. 08Pitfalls That Break Parallel Programs: Deadlock, False Sharing, and Data RacesNow that we've seen how parallel execution can accelerate our work, let's talk about the hidden dangers that can ruin it. This slide covers three classic pitfalls. First, deadlock. Imagine two threads, each holding a key the other needs. Thread A has lock X and waits for lock Y, while Thread B has lock Y and waits for lock X. They freeze forever, a permanent standstill. Second, data races. This happens when threads access shared data without coordination. Think of two bank tellers reading the same account balance at the same time, then both writing a new value. The final result is non-deterministic and usually wrong. These bugs are nightmares because they often hide during testing and appear only in production. Third, false sharing. Even when threads touch completely independent variables, if those variables sit on the same cache line, the CPU forces them to fight over ownership. The cache line bounces between cores, causing severe slowdowns that make parallel code slower than serial code. To survive these pitfalls, use tools like Thread Sanitizer to detect data races, stress-test your code, and watch for scaling plateaus that hint at hidden contention. Coming up next, we'll see how these parallel concepts power the everyday software you use in web browsers, video processing, and games.Pitfalls That Break Parallel Programs: Deadlock, False Sharing, and Data Racesdevelopers-heaven.netalgomaster.ioembeddedprep.com+22 min
  9. 09Parallelism in Everyday Life: Web Browsers, Video, and GamesLet's connect all of this to the devices and apps you use every day. Think about your web browser. When you open a new tab or run an extension, a modern browser isolates that work into its own process. Each tab becomes an independent task, running safely on its own core. It is a clean example of task parallelism—distinct jobs happening at once, protecting both performance and security. Now, consider video editing. Here we see data parallelism shine. The tool decodes thousands of frames, applies color effects, and encodes the final stream. It distributes segments of the video across all your cores, so every worker processes its own slice of the same operation at the same time. Video games blend both patterns beautifully. The game runs physics simulations, artificial intelligence, and audio processing on different CPU cores as separate tasks. Meanwhile, the GPU renders complex graphics by splitting the scene into millions of pixels and processing them in massive parallel waves. Even software you might think is simple, like spreadsheets, uses parallelism. When you recalculate thousands of cells, the engine can evaluate independent formulas concurrently. Spell checkers scan different sections of your document in parallel. All of this happens invisibly to keep your interface fast and responsive. Next, we scale up from your personal device to the global cloud, where parallelism reaches an entirely new level of power.Parallelism in Everyday Life: Web Browsers, Video, and Gamesintel.comkindatechnical.compatterns.eecs.berkeley.edu+22 min
  10. 10From Laptop to Data Center: Cloud-Scale ParallelismNow we scale up. We have seen how one machine juggles tasks through shared memory. But what happens when the data outgrows a single computer? That is where we move from the laptop to the data center. On your local machine, tools like OpenMP, Python's multiprocessing module, or Java's ForkJoinPool directly read and write shared memory. It is fast, but it is locked to one physical box. Once we cross into clusters and the cloud, the memory is no longer shared. We rely on message passing. Frameworks like Apache Spark and Hadoop MapReduce distribute the workload across hundreds of machines using network messages. This inherently avoids the single-box limit but introduces different bottlenecks like network latency. The real power often comes from a hybrid approach. We use M P I to coordinate across different nodes in the cluster, and then inside each node, we use OpenMP to manage the local threads. This gives us massive scale without losing local efficiency. So, how do you choose the right tool? If you have a small, tight computation on one machine, stick with shared memory threads. If you are processing terabytes of data, you need a distributed framework. The trick is matching the tool to the scale of the problem. Next, let us see how we actually measure if that scaling is working, as we dive into Evaluating Parallel Performance, Speedup, Scaling, and Efficiency.From Laptop to Data Center: Cloud-Scale Parallelismadhdecode.comida.liu.sescalemind.dev+22 min
  11. 11Evaluating Parallel Performance: Speedup, Scaling, and EfficiencyNow let’s translate everything we’ve discussed into measurable results. We need a way to know if our parallel program is actually paying off. That’s where speedup, scaling, and efficiency come in. Speedup is simply the sequential time divided by the parallel time. If it took one hundred seconds alone and twenty seconds with four cores, that’s a speedup of five. But linear speedup, where doubling cores exactly doubles the speed, is very rare in practice. There are two main scaling strategies. Strong scaling keeps the problem size fixed and adds more cores. It’s like hiring more chefs to cook one fixed meal faster. This is quickly limited by Amdahl’s Law because the serial steps can’t be split. Weak scaling, guided by Gustafson’s Law, keeps work per core constant. You grow the problem size as you add cores, like asking each new chef to cook a full new meal. Finally, we measure efficiency. That’s speedup divided by the number of cores. When you keep adding cores, overheads for communication grow, and efficiency drops. So our goal is finding that sweet spot where extra cores still give a meaningful gain. Next we’ll look at deciding when, and when not, to parallelize.Evaluating Parallel Performance: Speedup, Scaling, and Efficiencyadhdecode.comida.liu.sescalemind.dev+22 min
  12. 12Deciding When—and When Not—to ParallelizeSo, after all this talk about how to parallelize, we need to address a critical question: when should you actually do it? Parallelizing isn't free. It adds management, communication, and synchronization overhead. You should only commit when the speedup you gain clearly outweighs those extra costs. Before you dive in, run through a quick checklist. Is your workload large enough to justify the effort? Are the tasks mostly independent, with minimal data dependencies? And crucially, is the serial fraction of your code very small? Even a small serial chunk creates a hard speed limit, thanks to Amdahl's Law. Also, watch out for common anti-patterns. Don't parallelize work that is bound by input or output speed; you'll just create a traffic jam instead of a faster pipeline. And avoid creating far more threads than you have physical hardware cores. That oversubscription forces the system to waste time switching between them instead of doing real work. The golden rule is this: profile first. Use a profiler to find the true, hot-spot bottlenecks consuming your run time, then selectively parallelize only those parts. A surgical approach is always better than guessing. Next up, we'll pull all these concepts together in our final segment: Wrap-Up and Your Learning Pathway Forward.Deciding When—and When Not—to Parallelizeadhdecode.comida.liu.sescalemind.dev+22 min
  13. 13Wrap-Up and Your Learning Pathway ForwardWe have covered a lot of ground together. To wrap up, remember the core workflow: decompose your problem into independent tasks, communicate the right data, synchronize only when necessary, and then measure your actual speedup against the limits we discussed, like Amdahl’s Law. That sequence is your new superpower. For your immediate learning pathway, start with threads in a language you already know like Python, Java, or C plus plus. Getting comfortable with shared-memory parallelism there is a perfect launchpad. From that base, you are ready to explore OpenMP for easy loop-level speedups, and then step up to GPU computing with CUDA or tackle distributed clusters with frameworks like MPI and Apache Spark. And here is the most important takeaway: the way of thinking matters more than any single tool. The principles of independence, coordinated communication, and designing for scalability are what make you a true parallel programmer. Thank you for joining me on this introduction to parallel computing. I can’t wait to see what you build with this powerful new mindset.Wrap-Up and Your Learning Pathway Forward2 min

Sources consulted

Web sources consulted while building this course.

Parallel Computing Fundamentals