Wed 6 Feb 2008
Every now and then, a blog article appears on how to do “Erlang-Style Concurrency” in language X. I think this is a very good thing, as it indicates that people are starting to take a long hard look at concurrency.
However, there doesn’t seem to be any authoritative definition of the term “Erlang-Style Concurrency”. I thought I’d at least try to give my view.
Joe Armstrong writes the following in his book, Programming Erlang:
“In Erlang:
- Creating and destroying processes is very fast.
- Sending messages between processes is very fast.
- Processess behave the same way on all operating systems.
- We can have very large numbers of processes.
- Processes share no memory and are completely independent.
- The only way for processes to interact is through message passing.”
I guess this is about as concise and authoritative definition as any. However, it’s not the whole story.
I would add the following items:
- Message passing is asynchronous.
- Processes can monitor each other.
- It is possible to selectively receive messages.
- Remote processes appear largely the same as local processes.
So this roughly describes how concurrency works in Erlang. Are all these bullets necessary for Erlang-style Concurrency? Perhaps not.
I would pick the following items as necessary for Erlang-style Concurrency:
- Fast process creation/destruction
- Ability to support >> 10 000 concurrent processes with largely unchanged characteristics.
- Fast asynchronous message passing.
- Copying message-passing semantics (share-nothing concurrency).
- Process monitoring.
- Selective message reception.
A few words to explain the above:
Speed and scalability
In order for concurrency to be useful as a fundamental modelling paradigm, the programmer must feel that processes are cheap enough you can efficiently create as many processes as the problem calls for. If there is any single defining characteristic of Erlang-style Concurrency, it is that you should be able to model your application after the natural concurrency patterns present in your problem. If creating a process is perceived as expensive, programmers will reuse existing processes instead; if message passing is perceived to be expensive, various techniques will be invented to avoid sending messages. These workarounds are nearly always detrimental.
There will always be limits. Erlang was designed for agent-style concurrency; not for massive data parallelism. You can “only” have ca 120 million concurrent processes in Erlang, provided you have enough memory. Personally, I’ve verified that Erlang performs consistently up to 20 million concurrent processes (I didn’t have enough memory to go further.) The cost of creating an Erlang process (ca 5-10 us) will be prohibitive for some applications, but it’s at least an order of magnitude cheaper than creating UNIX processes or POSIX threads. It’s reasonable to expect Erlang-style Concurrency to use roughly the same process granularity as Erlang does.
Asynchronous message passing
It has been argued that synchronous message passing is a better building block than asynchronous message passing. It is probably true that synchronous message passing is much easier to reason about, but asynchronous communication (”send and pray”) feels more intuitive in a distributed environment (in which case environments based on synchronous message passing will resort to some form of asynchronous communication as well). Anyway, it is reasonable to argue that asynchronous communication is a defining characteristic of Erlang-style Concurrency.
Copying semantics
Note that this doesn’t necessarily mean that messages must be copied, but they must act as if they were. There are several reasons why this is important:
- For reliability reasons, processes must not share memory
- In the distributed case, copying is inevitable, and we want as similar semantics as possible between local and distributed message passing.
It is perhaps too strong to require that processes share nothing, as this would rule out Erlang-style concurrency in most existing languages (even languages like Scala make it possible to share data between processes). Let’s just say that the Erlang style is to make it easier to use share-nothing concurrency than to use sharing.
Process monitoring
This is the enabler of “out-of-band” error handling, or the “let it crash” philosophy, which is very typical for Erlang. The idea is that you program for the correct case, and rely on supervising processes for error handling. This has turned out to be a beautiful way of handling fault tolerance in large systems.
Selective message reception
There are many ways to achieve selective message reception, but for complex multi-way concurrency, you have to support at least one of them. This is something often overlooked when approaching concurrency. It’s not obviously needed in simple programs, but when it is needed, you will suffer complexity explosion if you don’t have it.
I would suggest that ability to support selective message reception is sufficient for Erlang-style concurrency. It can be done either by matching on a single message queue (Erlang, OSE Delta), or by using different mailboxes/channels (Haskell, .NET, UNIX sockets). Pick one, and find a suitable example to experiment with. You may use my presentation “Structured Network Programming” (also attached as pdf) as inspiration, if you like.
February 6th, 2008 at 3:34 pm
[...] bookmarks tagged languages Ulf Wiger: What is Erlang-Style Concurrency? saved by 5 others laxman2414 bookmarked on 02/06/08 | [...]
February 6th, 2008 at 8:56 pm
Hi, Ulf -
Nice article. Do you happen to have that presentation in a friendlier format, like PDF?
February 6th, 2008 at 9:41 pm
[...] be experimenting with adding Erlang-style concurrency to other languages, Ulf Wiger has written a nice explanation of what Erlang-style concurrency actually is. Definitely [...]
February 7th, 2008 at 12:52 am
Ulf, thanks for the great article. I would another condition, which although it doesn’t affect the coding style, it ensures “peace of mind” when writing applications in a language that uses lightweight processes: the guarantee that a process will never block the scheduler for a long time (e.g. for doing IO or calling native code). This is achieved in Erlang by the event-driven network IO core and the port-based native code interface. If those processes could call functions that block the emulator for more than a few milliseconds, performance could seriously suffer. When your language uses OS threads, this condition is a given, but when the VM does the scheduling, more restrictions are required on interactions with native code. All non-Erlang languages/libraries I know that supposedly implement Erlang-style concurrency lack this feature.
February 7th, 2008 at 1:46 am
[...] Wiger这个名字,他是 Erlang 最初的开发者之一。最近他写了一篇博客《What is Erlang-Style Concurrency?》对于“Erlang风格的并行”发表了自己的看法,粗浅译来,给大家共享。 [...]
February 7th, 2008 at 6:32 am
Bryan - I updated the article to also include a PDF version of the presentation.
February 7th, 2008 at 6:38 am
Yariv - I was thinking about whether to include scheduling characteristics. It’s sort of related to Joe’s “processes behave the same way on all operating systems”. A classic problem with Java was that it didn’t specify whether scheduling should be cooperative or preemptive. Of course, if you want to build the language on top of a concurrency paradigm, you must nail those things down. I’m still undecided, though. I wanted to keep the listing reasonably short, and focus on what I think qualifies as “Erlang-style”. (:
February 7th, 2008 at 10:44 am
I always interpreted “Erlang-Style Concurrency” to mean: the actor model of concurrency. (As opposed to the shared-memory model of concurrency.)
February 7th, 2008 at 12:10 pm
DAR - you’re not wrong, of course. The question is just how much one should read into “Erlang-style concurrency”. I thought that one should raise the bar a little bit, to highlight the fact that there is much more to Erlang and concurrency than just copying asynchronous message passing.
February 7th, 2008 at 1:02 pm
I have been programming using Scheme48, a Scheme system that offers a Concurrent ML-like library [1]. A while ago, there was some excitement over Termite, another Scheme system that’s supposed to offer ‘Erlang-like’ concurrency. I never got around to using Termite, and I never figured out the difference between the two approach.
I saw that you once made a posting to LtU on this topic as well, could you elaborate on the difference between the two?
[1] http://mumble.net/~campbell/s48-refman/html/Concurrent-ML.html
February 7th, 2008 at 1:38 pm
Dataflow technology implements processes and queues between the processes, very similar to Erlang. Dataflow is very well suited for large data processing. I’m not all that familiar with Erlang, but don’t believe it is as well suited for such processing. Dataflow is very much a data-oriented architecture, a new way of problem decomposition. Check out http://www.pervasivedatarush.com for more information.
February 8th, 2008 at 5:46 am
[...] Erlang-style concurrency In order for concurrency to be useful as a fundamental modelling[sic] paradigm, the programmer must feel that processes are cheap enough you can efficiently create as many processes as the problem calls for. [...]
February 8th, 2008 at 6:04 am
Duncan - I’ve never used Termite (nor CML), but Termite is one of the most ambitious attempt at implementing Erlang-style concurrency in another language (Glasgow Distributed Haskell is up there too). CML relies on synchronous message passing, and (too my knowledge) doesn’t implement distributed message passing or process supervision. Termite even shows a way of doing process migration - something that Erlang cannot do. I don’t want to make a qualitative comparison, since I’m not that familiar with either. The concurrency support in CML is designed to fit well into the ML language (to ML users, this is obviously a significant advantage). I don’t know what that buys you in Scheme, or whether Termite is a better fit with Scheme overall.
February 8th, 2008 at 6:17 am
Jim - You’re right in noting that Erlang may not be ideal for large data processing. The main problem may not be the concurrency model, but rather dynamic typing, which gives the compiler less information to work with. I think Haskell may have much better potential in this area, and some very interesting research has certainly been done http://www.haskell.org/haskellwiki/Research_papers/Parallelism_and_concurrency) and Simon Peyton-Jones’ slides, http://research.microsoft.com/~simonpj/papers/ndp/NdpSlides.pdf, give a pretty good idea of what’s possible when using Haskell for data parallelism.
There is another take on dataflow programming, which perhaps would feel more natural in Erlang: Flow-based Programming (http://www.jpaulmorrison.com/cgi-bin/wiki.pl), but this seems different from what you’re talking about.
February 8th, 2008 at 6:48 am
[...] over the concurrency features of Erlang, the language famed for nine 9’s of uptime, I find that nServiceBus covers almost every [...]
February 8th, 2008 at 12:20 pm
I’m a researcher at the University of Kent, working with a parallel language called occam-pi (http://occam-pi.org/).
It’s interesting to note that occam matches the first set of bullet points above (millions of processes, nanosecond communication/process creation times, compiler-enforced alias freedom, etc.), but not the second — communication is synchronous, there’s no process monitoring, and the last two don’t really apply because we have explicit channels rather than addressed messages.
We usually describe occam-pi as a “process-oriented” language, but this suggests that you can be more specific. Perhaps both occam-pi and Erlang are in the larger class of process-oriented languages (which makes sense given how similar they are in many regards), and occam-pi uses a “channel-oriented” communication model whereas Erlang is “message-oriented”…
February 9th, 2008 at 3:59 am
Adam - I agree. As for the explicit channels, this allows Occam to avoid “accidental ordering” that can happen if you collect messages from multiple sources in a single mailbox. I think the term “channel-oriented” is good, but I’m not sure what would properly describe the Erlang approach of pattern-matching on a single message queue. (:
I like Occam a lot. It was the second concurrency-oriented language I ever learned (the first was Concurrent Euclid).
February 11th, 2008 at 1:22 am
[...] Ulf Wiger » What is Erlang-Style Concurrency? (tags: erlang programming) [...]
March 17th, 2008 at 10:35 pm
[...] Message passing model (a functional language implementation) (for more on Erlang concurrency, see this). While people may be envious of what they see as “perfect side effect free concurrency” - I doubt [...]
June 14th, 2008 at 2:18 am
erlang-style concurrency is posix style process management with error handling.
June 14th, 2008 at 3:05 pm
What I can’t find anywhere: Is multi tasking in Erlang cooperative and do task switches happend during messages passing?
Peace
-stephan
June 14th, 2008 at 7:07 pm
The PDF version of your presentation crashed Foxit Reader moving from page 3 to 4.
June 17th, 2008 at 8:55 am
@Bob: I’m sorry. I don’t have Foxit Reader, and am not sufficiently knowledgeable about the PDF format to determine the source of the problem. It works in Acrobat Reader, as far as I can tell.
June 17th, 2008 at 9:04 am
@Stefan: Erlang uses preemptive scheduling, at least as far as the user is concerned. In the traditional, single-CPU implementation, scheduling was reduction-based and very predictable. In multicore Erlang multiple scheduler threads are premptively scheduled, meaning that scheduling becomes truly preemptive from the user perspective.
Message passing does not force a task switch. The receiving process is marked as runnable, but the sending process may well be allowed to continue (unless it is scheduled out for other reasons).
June 17th, 2008 at 9:09 am
@web design company: This is true in a way, except for the fact that erlang processes are much more lightweight than POSIX processes. This has profound implications on how processes are used in system modeling.