Parallelism /= Concurrency
ghcmutterings.wordpress.com
ghcmutterings.wordpress.com
Let's look at the definitions given:
A concurrent program is one with multiple threads of control.
A parallel program, on the other hand, is one that merely runs on multiple processors
Well, you have to have multiple threads of control to run on multiple processors simultaneously. And if you're running on multiple processors simultaneously, you have multiple threads of control.
So what's the difference?
If anybody can enlighten me on why parallelism != concurrency (a claim I've seen often), I'd appreciate it.
By the way, to me, all programming languages can be concurrent because ultimately it's all just opcodes; and the word "non-determinism" is problematic anyway. So there's some context for you.
Parallelism is something more concrete, it REALLY exists in the real world. A programmer could just imagine and write a sequential loop, but if the compiler deemed it to be really parallel, it could really run parts of the loop in parallel. Of course, a programmer could write a concurrent program, as in, multiple threads of control, and the system could really run it in parallel, say on different cores of a multi-core processor. So remember, concurrency is abstract, parallelism is concrete.
Lots of people saying slightly different things, and no way for me to know where consensus lies.
So you might argue that concurrency is necessary for parallelism, but it's not sufficient.
Concurrency, to me, is about expanding the set of sequentially-equivalent executions of the program which are considered 'correct' according to the operational semantics of the programming language. If you just have one correct sequential execution, then there isn't any concurrency because you would have to execute sequentially to enforce the one correct ordering.
For example, consider this snippet:
1. a = 1 2. b = 2 3. print a to file1 4. print b to file2
For most realistic language semantics there's no dependency between lines 3, and 4, so it's ok to execute in the following orders:
1, 2, 3, 4 1, 3, 2, 4 2, 1, 3, 4 2, 1, 4, 3
and others. There is concurrency there, and if the runtime cannot detect or leverage it quite often the processor will with out-of-order-execution and pipelining.
However, the simpler snippet:
1. a = 2 2. b = a * 2 3. print a + b
Doesn't have any obvious concurrency (ignoring that in this case the compiler could optimise away the assignments and additions into a single statement). 1 must happen before 2, which must happen before 3. There is only one 'correct' sequential execution, and therefore there is no obvious parallelisation achievable.
Well, I disagree that "concurrency" should be defined as "having multiple threads," but if that's how we want to define it...
then I think you conflated the definitions half way through. Your final example, you state, has neither concurrency nor parallelism.
Actually, by your definition of concurrency, it could be concurrent (put each statement in a separate thread and use locks), though I agree that it can't be parallel; it's inherently sequential.
Two separate threads that can run completely independently without any synchronisation are concurrent because it doesn't matter what order you run them in. Therefore there are many, many 'sequentially equivalent' computations that are correct per the language semantics. From the perspective of the user it can seem like you ran Thread A to completion followed by Thread B, or Thread B then Thread A, or any interleaving of the two.
Threads are one way of expressing concurrency. Actors, to pick at random, are another. So I don't believe that threads <=> concurrency, but that threads usually express concurrency unless they have pathological synchronisation behaviour.
Which they would have, in your final example - there is no concurrency because there's only a single ordering of events that is correct.
I like your Actors example. Yeah, concurrency can't be defined in terms of "threads" in a specific implementation sense, but only in a more generic sense of "threds of execution," as in "execution contexts".
Concurrency occurs when program semantics allow two threads of execution to be executed in a simultaneous or interleaved manner.
Parallelism occurs when two program threads actually execute simultaneously.
Concurrency is a programming model that lets you express things that are independent, as independent executions
...and parallelism is about running two things at the same time.
Parallelism's goal is to max out the overall throughput. It deals with partitioning data and algorithm to keep all the CPUs busy while minimizing the inter-CPU communication cost. It deals with efficient data transfer, cache coherency, data locality (out of the 10K CPUs, your neighbors can faster serve your subtask's need), and data dependency (which nodes need to go first). Parallel programs can be surprisingly deterministic. Some examples are cracking password (partition the dictionary in N ways and have N CPU hitting them), web crawling (partition the URL address space N way and have N CPU hitting them). Most of the map-reduce stuffs are parallel programs.
Concurrency's main concern is to bring consistency to the non-deterministic nature of multiple threads of execution. Mutex, semaphore, event, monitor, and transactional memory are some mechanisms to bring order to the chaos.
Concurrency occurs when program semantics allow two threads of execution to be executed in a simultaneous or interleaved manner.
Parallelism occurs when two program threads actually execute simultaneously.
By the way, I still have problems with the use of "non-determinism" here. Contrary to what you say, schedulers do not schedule threads randomly. But looked at from the perspectice of program semantics, that might as well be the case, which is (I think) the reason people keep bringing that up.
The goal of writing a proper concurrent program is no matter what the order of execution, the computed outcome is the same, due to consistency guarantee coded in.
BTW, I'm going to pick on you a bit. I don't like it when people oversimplify things in CS. I think it can be confusing to beginners (and, sometimes, non-beginners). I don't like when people say things like "the scheduler can do whatever it wants," "the OS scheduler can change any time," "the scheduler is non-deterministic," etc., because I know those things aren't true. They may help make a point, but it's also introducing a source of potential confusion.
I say this from the perspective of somebody who has actually done some work on OS schedulers. I mean, to me, the OS never substitutes a new scheduling algorithm any time it likes, though an OS hacker may actually change the algorithm. And, as you may know, kernel hackers take advantage of known limitatations in concurrency (i.e., concurrency that will never "actually happen").
Note that I never said the scheduler is non-deterministic. It is the order of your threads' execution that's non-deterministic.
There are lots of schedulers out there that have a different contract than the one you described ("run threads in any order"). For example, many real-time systems run threads in priority order - you only get preempted if a higher-priority thread wants to run.
Imagine you spin off 10 threads waiting for network requests where one thread per client machine. It cannot be deterministically decided which thread will run first and which will run next. It depends on which client connects to which thread first. That's the non-deterministic nature of the program. Concurrency is the discipline to deal with that problem.
That depends on the semantics of your programming language. You may have a different perspective than the author, but I think his explanation is clear.
Edit, To add a concrete example: I am currently working on a concurrent Haskell program that simulates a system of communicating nodes. However I am running it on my single core netbook. So we have indeterminacy and concurrent semantics without parallelism.
Well, not being someone from the functional programming world, I don't think the explanation is clear at all.
Edit, To add a concrete example: I am currently working on a concurrent Haskell program that simulates a system of communicating nodes. However I am running it on my single core netbook. So we have indeterminacy and concurrent semantics without parallelism.
So you're saying that concurrency is an issue of semantics used in programming, while parallelism is talking about the underlying problem?
I might be able to agree with that, but if that's the argument, that is what the author of the blog post should have stated.
Anyway, please let me know if I'm understanding your point.
Nonetheless - I understand concurrency to be a notion such as coroutines, and parallelism to mean, you know, things happening at the same time.
If you're designing a programming language or OS you're probably more worried about concurrency, but if you're using that programming language or OS to solve a computational program you're more worried about parallelism.
For the sake of discussion, I'm going to use the term 'module' instead of thread, process, function, object, etc.
I think by current programming he means that you have multiple modules 'near' the same resource which means the multiple modules are near each other. Same as if you and I are both wanting to use the same physical resource, by us both being near that resource we are near each other (i.e. things that I do to the resource affects you and vice versa).
By parallel programs I think he means that the multiple modules are never near the same thing and thus not ever near each other.
By parallel programs I think he means that the multiple modules are never near the same thing and thus not ever near each other.
I think what you're saying here is that some problems are "embarassingly parallel," in the sense that the problem can be broken into many completely independent sequential computations.
But I don't think that's what the guy who wrote the article was talking about. If so, I think he would have said so.
Edit:spelling.
Let S be the set of (addressable) storage locations, let d(x, y) = x - y which is the distance between storage locations (an integer). The triangle inequality holds if d(x, y) + d(y, z) >= d(x, z). d is also clearly symmetric and reflexive; thus S and d form a metric space.
Suppose module A contains a pointer to storage location x.
Suppose module B contains a pointer to storage location y.
Suppose A and B each contain a function which reads from z and writes to x or y respectively.
If, with respect to A, d(x, z) = n < infinity, then, using pointer arithmetic, A can write to z and thus write to y. The distance between A and B is bounded (effectively it's n).
On the other hand, if, with respect to A, d(x, z) = infinity (the value infinity might mean d threw an exception or something), then the distance between A and B is effectively infinite.
So the concurrency/parallelism dichotomy boils down to this:
Programs in which such distance between modules is finite are concurrent, programs in which the distance between modules is infinite are parallel.
This example only applies to memory but it can easily apply to other kinds of relevant spaces as well.
Parallelism: simultaneous calculations executing in the service of a single problem, usually with the goal of improved performance.
Concurrency: executions in the same time granularity, but not necessarily simultaneous. Also not necessarily in the service of the same problem, but some form of synchronization is required.
(Admittingly repeating myself: http://news.ycombinator.com/item?id=2387067)