Ask HN: Multiprocessing & concurrency: C, Python, Clojure (& Go :))?
I've never looked into Clojure so far (sorry :) but how does this compare to OpenMP(I)? Or even with Python's parallel processing capacities? How much would performance difference vs. implementation ease be between a C MPI and Clojure parallel processing implementation? Or how would Clojure's MP libraries compare to Python's multiprocessing.JoinableQueue and Manager? Would it be worth considering learning Clojure? And, yes, I know I should learn a new language per year, but then I would at least like to know how it compares to Go - as I am considering that because of the channels, but I now often hear Clojure named alongside Go for exactly those reasons.
So, just from the MPI perspective: Clojure, maybe Go, and Python or C(++), what are you using and why?
42 comments
[ 3.5 ms ] story [ 107 ms ] threadFor concurrency (short version: this means multiple processes doing different things sharing some small amount of state), I'd say Clojure, Go, and Python are all good choices. Really, anything with either immutable data structures and good IPC support (Clojure has this, agents are fantastic for IPC), or coroutines (Go, Python, Lua...) will give you great concurrency control.
For multiprocessing/parallel processing (short version: this means multiple copies of the same processing working on smaller parts of a large data set), OpenMP is a great way to go. I have gotten a lot out of Clojure and Hadoop (on a large cluster), and the new pvmap/pvreduce look like they'll help a lot on the single node, multicore front. I would not trust Python to do these things fast, and I can't tell you about Go. If you can partition your data successfully and prevent GIL problems, Python will do fine.
EDIT: I maybe better should say, how much easier it is to use. After, all it is a JVM, so it will be slower than C, obviously, but you also get rid of the million of possible bugs you can introduce in it (which makes using Python so attractive to me). So, is there a good reason for somebody who as used either Python or OpenMP for multiprocessing to switch to Clojure?
You should switch to Clojure from Python because it's faster.
You should switch to Clojure from C because it's easier, and more specifically, easier to get it to a certain level of speed (which is high).
Don't let the JVM fool you into thinking it's slow. It isn't.
First of all, should performant code in your language be "seldom observed" and require the knowledge of the "gurus"? I should hope not.
I have written plenty of optimized python in my day, and it's always been a decent constant behind c or java, no matter how many of the "best practices" I follow. Pypy may be a different story, but their toolchain was too complex and slow last I looked.
Most of python's best practices are language bugs, anyway. Generators are great, but most of the rest is crap. I should cache my namespace lookups outside the loop? Why can't you do that for me, Python? That's one of the simplest compiler optimizations we have. All the fastest Python is written in C extensions. Why do you think this is?
And no, I am not saying that Python outperforms Java, obviously. But it can provide very decent performance at very low LOCs. And then you can identify the bottlenecks and implement that part in C. Overall, a much faster approach than writing in Java (and, obviously, than writing in pure C) with final performance rates beating a JVM easily.
EDIT: No, that is actually even too complicated than what I mostly need to do: For the real bottlenecks I usually just need to find the C library that solves that hard part (e.g., some number-crunching stuff I am doing) and already has a Python wrapper, or you generate one (hello SWIG), or, worst case, you really have to write one (yet have to find that case, although).
I know that wasn't one of your 4 mentioned languages, but it's worth a mention.
Erlang is really good at making the coordination layer efficient (the message passing and threads are fast) as well as usually rather clean. The original use-case for Erlang, telecomunication switches, are written in a mix of Erlang and C. The low level stuff (handling call-data and similar things) is handled by C, and the high-level structure of the program is in Erlang.
Anyone care to tell us about how the two languages stack up against each other?
The Clojure slide deck that's currently featured on the front page has some useful definitions:
"Concurrency is commonly mistaken for parallelism, but the two are distinct concepts. Concurrency is concerned with managing access to shared state from different threads, whereas parallelism is concerned with utilizing multiple processors/cores to improve the performance of a computation."
Haskell has parallelism nailed. You can basically tell the compiler, "I want these two functions to run in parallel on two different cores." The compiler will then transparently do some fork/join type magic, and automatically enforce that the functions can't step on each other's toes. (It probably uses threads instead of processes, but since the functions are not allowed to share data, it feels like Unix-style fork/join.)
(Side note: Unless it's explicitly stated in the function's signature, Haskell functions are forbidden by the compiler to have any side effects. This will drive programmers coming from a procedural background absolutely insane, but it makes the above magic possible.)
I haven't coded in Erlang, but it seems to rely heavily on message passing. The compiler stops you from doing stupid things, but the concurrency is still exposed to the application.
So, if your only goal is to distribute your workload to as many cores as possible (say you're building a video encoder, or in my case a ray tracer) with as little hacking as possible, Haskell will make your day.
If the existence of different threads is part of your actual application architecture, Erlang is your friend. It lets you explicitly create producers and consumers, and play around with different event architectures.
As mentioned before, I'm a Haskell n00b and completely unfamiliar with Erlang, so if anyone wants to chime in and correct me, please do.
I'll also say that Haskell has a really steep learning curve, especially if you're coming from a procedural/object oriented background. I've programmed in Scheme and ML before, and neither were as mind-bending as Haskell.
I haven't done any projects yet that involved mixing Haskell with another language, so I can't comment on that. That's probably the most sane way to do a large app with Haskell--use Haskell for what it's good at, and use C/Python for what they're good at.
If I had heard about Erlang's concurrency support earlier, I might have gone with it instead of Haskell, because Haskell is so hard to learn. Now that I'm already partway over the Haskell learning curve, it seems nice enough to stick with.
I really can't say yet whether Haskell is worth learning. Keep in mind that it will be hard to bring new programmers on board with a large Haskell code base. (I'm using it for hobby projects, so that's a non-issue.)
The best answer I can give is: If you're going to be writing large swaths of code that make few or no system calls and that are CPU intensive, it might be; otherwise, no.
EDIT:
Let me try to clarify what I mean by the "mind-bendingness" of Haskell. Think about the Integer and String objects in Java--once instantiated, they are immutable. All data in Haskell behaves like this. However, this immutable data lives on the stack, so you can't just reassign the reference to a new object. (If you want to increment your parameter x, you have to make a recursive call with x + 1.)
The exception here is a construct called a monad. Inside a monad, you can reassign references to new objects, and the old objects are cleaned up by the garbage collector. The intermediate objects are invisible to code outside the monad. (As an example, I/O is done inside of monads.)
The two things I like about Haskell are the concurrency support (if you can wrap your head around the above paradigm!) and the syntax (you can do Python-style significant whitespace, or C-style semicolons and brackets, your pick). Unfortunately, Haskell manages to screw up almost everything else.
Though it's entirely possible I screwed something up or missed an important bit.
As far as performance vs implementation here is a handy chart http://shootout.alioth.debian.org/u32/code-used-time-used-sh... That's for generic language things, but concurrency probably doesn't stray far from the overall performance.
So Go, Clojure, F#, Scala, Python could proabably be lumped into the same ease of implementation category as far as concurrency. They'll all make a mess if you have complex control flow. This http://news.ycombinator.com/item?id=1824683 (which you've already seen) or http://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workf... is what I call a mess, but it's as clean as it gets for now.
I think the real test is whether people start using concurrency as the norm and not as a separate use case (since it's annoying to write). I don't think these languages will have that effect.
The multinode users love MPI because it screams over infiniband. These are people running genome mapping simulations or fluid mechanic simulation etc. This is actually a minority of our users. Most of our users are perfectly content with our dual socket hex core westmere cluster. They use various applications for their simulations, but most of them have difficulty scaling past 12 cores anyway.
So, in my experience, MPI is great because the hardware becomes the limiting factor, and the other implementations are a little more software bound. So if you have a couple hundred cores available for a single job, you are stuck with MPI. If you are sticking to a multithreaded implementation, the other languages you mention might be a good solution.
It's becoming a bit of a problem, because we know they have terrible inefficiencies in algorithms and are not very well optimized for our system. But a ME Professor is more interested in the splashback effects supersonic turbines create than how efficient his simulation is. If it takes an extra 100,000 CPU hours, thats only an extra week for a 500 processor job. As long is it works well enough, they generally don't want to touch it.
So in that climate, there is very little research done using languages other than fortran and C. When it does it usually it comes about as a result of an interesting library that does X and is written in Y.
We are a small shop, especially for a 10,000 core system. We've only got 3 full time employees and three student employees:a part time web dev, a part time hardware tech, and me (Also part time.) . I've been trying to spend my time working with users and optimizing their code but for the most part they just aren't that interested. I think the prevalence of fortran shows a lot about the overall mentality. Just use what works, and get as much use out of it as you can.
they refer to it as sockets on steroids
* It's really natural if you work on an OS that has fork() (i.e., not Windows), you just need to create a Pool after you've initialized everything and can use Pool.imap (in much the same way as itertools.imap) after that. * Distributing data for initialization (which is ~100-200MB in my case) would add another level of complexity if I wanted to distribute tasks among several computers automatically.
At the granularity I'm working (most single tasks use >10ms, many >100ms), a simple worker pool works just as fine as the ForkJoin model that Clojure parallel is based on.
OpenMP and OpenMPI are for a very different use case where you know (pretty much) exactly how long the different parts of an operation will take, so you can make different processors (or different nodes) take different, determined-in-advance, chunks of the problem. Usually, the "parallel map" abstraction implemented by Python's multiprocessing, Java's ForkJoin, or Hadoop/Disco is much more useful as a quick fix to parallelize something than if you start out with channels and try to build something on top of that.
TL/DR: It depends on your workload what multiprocessing workload is right for you. For most bread-and-butter workload where you can use a parallel map function, multiprocessing/ForkJoin is just fine; OpenMP is great if you have a very predictable split between processors and are willing to jump through additional hoops to squeeze out the additional 5-10% of performance. Don't use queues etc. unless you know you need them. Multi-node solutions (MPI, map/reduce) usually add another layer of complexity because you need to ship the initialization data to each node (if you need, e.g. a statistical tagging or parsing model).
If the timing is very predictable, OpenMP's parallel-for construct fits your bill very nicely.
According to the OpenMP tutorial, the default for OMP_SCHEDULE is implementation dependent, and the gcc OpenMP implementation uses dynamic scheduling with a chunk size of 1 by default.
Go's design makes much more efficient use of a single machine's resources than any JVM based language ever could. In terms of memory it is comparable to C or C++. Goroutines are very lightweight because they use a variable size stack, so Go supports much more fine grained parallelism. Whether you need that or not depends on your application of course. I found goroutines much simpler and intuitively accessible than the large set of Clojure's parallel primitives. But I think reasonable people can have different opinions on that one as STM (software transactional memory) is attractive too and immutable data structures do make some things simpler.
Of course, Clojure is a Lisp dialect, which has other benefits that have nothing to do with parallelism. However, for many of my own tasks, memory usage is critical and Clojure hogs memory even more than regular Java, so that's why I would prefer Go once it matures a little. I think Go can replace C and C++ over time. Clojure will never do that in its current JVM based implementation.
In terms of raw performance, if memory is not the issue, you can expect similar results from Clojure and Go, provided you make use of static type hints in Clojure (which doesn't look pretty) and you give Go's creators some more time to optimize the stuff that's known to be slow.
I have really taken a liking to the Erlang model. Processes share no memory and communicate by passing messages. There is no shared state locking/mutex.
Once I have more data/information (benchmarks, ease of development/integration etc.) I will publish my results.
There seems to be no language that truly would facilitate distributed computations (multicore & multiprocessor) significantly, however (maybe Haskell?). Is that really it, there is no language that is specifically apt for the new multi-core/-processor machines popping up everywhere that will make programming life easier for those architectures, especially when I want to do some algorithmic speed-ups? I am a bit disappointed, in a way.
http://blog.ezyang.com/2010/07/graphs-not-grids/
http://www.igvita.com/2010/08/18/multi-core-threads-message-...
http://erlware.blogspot.com/2010/07/brief-overview-of-concur...
http://www.sauria.com/blog/2009/10/05/the-cambrian-period-of...
http://www.slideshare.net/twleung/a-survey-of-concurrency-co...