In the Software world we often use terms concurrency and parallelism interchangeably. When someone tries to explain how some piece of code works they may use both terms and we generally know what kind of behavior the person is trying to convey. However there is a difference between concurrency and parallelism. I’ll try to explain it here as I see it.
Let’s start with concurrency.
There is this very known saying “We can walk and chew gum at the same time”. This is a good example of concurrency. We can even find ourselves walking, chewing gum and talking on the phone - 3 tasks at once! Us humans are pretty good at concurrency.
But this example is really about several completely unrelated tasks. But we can also take one bigger task and execute it concurrently.
For example, I love potatoes (it’s a Polish thing). When making potatoes we first have to peel them, wash them, cut them up and boil them (and maybe mash them up if this is how you like it served). One person can do all of the steps in sequence (sequential execution), but one person cannot do all or even some of those tasks at the same time. We’re just limited by the resources we have available, hands in this case.
We could prepare potatoes using a concurrent model by inviting a family member to help us. One person could peel the potatoes and put them into a bowl and get back to peeling. Another person could take peeled potatoes, cut them into pieces and put them in a pan of boiling water and repeat. We have two people now doing the process. With this approach prepping potatoes should take much less time.
What are the examples of this in software?
Think about workflows where you need to call 2 or more microservices before you can perform some other task. Without concurrency, you could call service A, wait for response, then call service B wait for response, then combine all responses and continue with some other task in the sequence. But this would be slow, your CPU would be doing nothing as it waits for network calls to complete. With a concurrency model you could call service A, not wait for response, call service B and wait for both calls to complete, combine responses and continue. This way you could potentially drastically improve response time in your application that heavily relies on network communication.
Another example. After a new account has been created you could make a call to an email service to notify new users of successful account creation and simultaneously publish an event to Kafka topic announcing to the internal system that a new account has been created. Again, these are just naive examples, there are cases where you may actually want such tasks to execute sequentially.
With parallelism we typically think of executing multiple same things at the same time. Going back to our recipe for making potatoes. How could we make potatoes faster using a parallel execution model?
We could call our neighbour next door and ask them to also make some potatoes! Both of us would be performing all of the same tasks of peeling, cutting and putting prepped potatoes in a pan of boiling water. This should work pretty well too and in theory we should be able to cut the time drastically as well.
There are some differences here however regarding usage of resources. In the first example we had one big bucket of potatoes, now we need two buckets and two peelers and two pans of boiling water.
Examples of this in software?
The simplest one that comes to mind is batch data processing. Think map/reduce type of computation as used in Hadoop and Spark frameworks. Each worker node receives a chunk of data to perform the same computation on, results are then combined into the final result on a driver node.
What else can we do? Well, we can scale this process in interesting ways. For example we can take our concurrent process of preparing potatoes and execute it in parallel, again with the help of our neighbour. We then could have 4 people total doing the work at 2 completely different stations prepping potatoes concurrently.
Concurrency and parallelism are slightly different concepts. Both models can be used separately or together to allow executing work faster with better utilization of compute resources.
I found this really good talk by Rob Pike - Concurrency is not Parallelism who explains it in the context of Golang, check it out.