Are Native Threads Worth It?
May 2nd, 2007 by phil
With the advent of multi-core CPUs concurrency is becoming more important, but is traditional threading the way to go? The problem is that traditional threading is very difficult to get right. I am sure that are some cowboys out there who will respond to that last sentence by saying that “real programmers” don’t have a problem with current threading models. Machismo aside, I have seen many smart, capable programmers write multithreaded code that deadlocks or corrupts data (including one of the aforementioned “real programmers”).
I took the Sun Certified Java Programmer test in 2004 to see what it was like. There were a lot of threading questions on the test. I remember reading somewhere at the time that Sun felt many programmers got threading wrong and thus felt the need to make it one of the 9 major areas the test covered. Also, some of the most frequent problems that developers run into when writing Swing code are related to threading.
Last month, there was an article on InfoQ the provided a good overview of the debate about threading in Ruby. It all started when blogger Ciaran McCreesh wrote a post about how Ruby’s green (or user-space) threads and the difficulty of interfacing with 3rd party libraries that use native (or kernel) threads. In this case, Ciaran was discussing the problems that can arise when interfacing with GUI toolkits which often rely on native threads to provide a responsive user interface. The article points out that Smalltalk implementations typically use green threads and then discusses some of the strengths and weaknesses of both approaches.
There have been some discussions recently with regards to Ruby’s performance, and it seems that the lack of native threads is a common complaint. In fact, James Edward Gray II asked Matz and Koichi about threading in the third part of his Ruby VM Serial Interview and the first comment had this to say:
Ruby’s future is at risk of becoming irrelevant as 2, 4, and soon 8 core solutions become common place.
The first assumption here is that as the number of CPU cores goes up that concurrency will be more important. This is hard to argue, but the second assumption is that something radical must be done to handle these levels of concurrency. Some people argue that the something radical is native threads which would allow the VM’s workload to be spread across multiple cores. Interestingly enough, the commenter then mentions Erlang as a language that seems to get it right.
This is interesting because Erlang’s concurrency model is closer to the “shared nothing” concept that Rails espouses than to the heavyweight threading model of Java. It is also interesting because Matz and Koichi indicated in the interview that they are exploring a “Multi-VM” concurrency model that is similar to Erlang. According to Koichi:
As matz say, if we have multiple VM instance on a process, these VMs can be run in parallel. I’ll work on that theme in the near future (as my research topic).
Erlang uses lightweight processes that communicate by passing messages. This approach is very similar to the way a Rails application is deployed on Mongrel or FastCGI: multiple independent processes that do not share state and communicate by passing messages via the database (if at all). I have a production Rails application that runs on a Dell server with two dual-core Xeon CPUs. We currently have 9 Mongrel instances running and all of the cores stay busy when the site is under load.
One drawback to the Rails approach is that communication is difficult and spawning new Mongrel instances is not fast or easy. Erlang processes have other advantages as well; from the Erlang FAQ:
Erlang processes are very lightweight, much lighter than an operating system thread. Switching between Erlang processes is typically an order of magnitude or two faster than switching between OS threads.
Each Erlang process is garbage collected separately. This avoids the (relatively) long delay which would occur if there was a single large heap for all processes.
Lightweight processes in Erlang also give rise to a novel error handling technique. In languages like Ruby and Java you do everything you can to avoid exiting the program unexpectedly. Typically, an application will throw an exception which will unwind the stack looking for a handler that can deal with the error. While this approach is much better than the earlier practice of having to check return codes for errors, it is still less than ideal. If the code that raises an exception is too far from the handler it can be difficult for a programmer reading the code to understand how the errors are handled. Also, the stack unwinding can be expensive and it may be difficult to continue after many types of errors.
In Erlang, you simply let the process die and a supervisor will start a new process and log the error. This seems to achieve the goal of exception handling (separating error detection and error handling) without the drawbacks.
I didn’t really intend for this to be a post about how Erlang is a great language. In fact, I have very little experience with it myself. The point I want get across is that there are multiple ways of solving many problems and often the accepted way isn’t the best way. Concurrency is going to be more and more of an issue as CPU cores scale up, but I doubt that threading will provide the best solution. Erlang shows how a fresh approach can get great results and I hope that Koichi follows through on his Multi-VM idea for Ruby.
Update: This post generated a surprising amount of interest. There is a short discussion on programming.reddit.com and murphee has an excellent followup post. Finally, in the comments Andre points to a great resource on concurrency and Erlang.



One of the most important aspects of the exception mechanisms in Erlang/OTP is the aforementioned supervisor can be running on an entirely different box and it works seamlessly.
An entire machine can explode in a ball of flames and a properly designed erlang app will yawn at it.
Sorry if that was a bit off topic.
-Scott
Actually, Scala, a relatively new language for the JVM, uses a Erlang-like lightweight processes model. You may especially find these two articles in the papers section interesting:
@Scott: Wow. That is seriously cool. Thanks for the comment.
@Michael: I have been aware of Scala for a while but it hasn’t really caught my interest yet. I am going to check out those papers.
I’ve got a few questions. Even if the processes are ultra lightweight, context switching for processes would still be slower because of the amount of state saving no? Also, threads share the same process memory and get their own stack so how can “communicate by passing messages via the database” be faster than communication by same or close paged memory?
Even if the database is loaded entirely into memory, I just don’t see how communicating through a database would be faster than communication within the process itself?
On the other side of things, you really didn’t say much about robustness, though it could probably go unsaid that more processes would be the way to go for robustness if this method was only as fast as threading.
Hopefully you can shed some light on this for me.
P.S. will this subscribe my email address to the comments so I know when I’ve been replied to?
@Luke: Erlang has a built-in message passing system for inter-process communication, so you don’t need to go through a database. Since a message consists of an arbitrary Erlang data structure, this is incredibly flexible.
As for robustness, Ericsson’s flagship telecomms switch runs 2 million lines of Erlang and has 9 nines uptime. That’s 99.9999999%…
Oops, I messed up the name/url thing. :-/
@Luke: It was late when I wrote this and I probably left out a lot of salient facts about Erlang and confused the issue a bit. Mea culpa.
The “processes” in Erlang are actually indepentent instantiations of the VM inside the same OS process. At least, that is how I understand it. Given this, starting new processes and context switching are very fast. Even though Erlang processes share the same OS process, they apparently have separate heaps which is good for garbage collection and helps keep the processes isolated from one another. As Jeremy points out, this makes Erlang very robust.
The “message passing” via database that I mentioned is with Ruby on Rails and it isn’t very good. It also isn’t a good analogue to the Erlang message passing. In the context of Rails it works because Rails processes normally don’t need to communicate with each other.
I hope this answers your questions.
Thanks for the explanation! That does seem very beneficial and I will definitely be looking into Erlang some more.
I think you’re confusing a couple of things here…
First, threads (as in, OS-level threads) are still essential. You must let the OS know about your threads to be able to load balance across multiple cores. (Of course, you can do this with multiple processes too.) If you use green threads only, the moment any thread makes a blocking I/O call, all your other threads block. Green threads are no substitute for OS-level threads.
Erlang uses a complex scheme of spawning one or more OS-level threads purely to handle IO. At any time, there’s one or more Erlang (OS-level) threads that is the language runtime and to handle computation, and one or more IO threads that will perform IO on behalf of the language runtime threads.
Small plug: I have some info about concurrency and Erlang on a webpage I wrote a while ago. You may be particularly interested in the Erlang book there, written by none other than Joe Armstrong himself :-).
@Andre: I agree 100% that OS threading in the language VM is essential. What I find interesting is the idea that a 1:1 mapping of language threads to OS threads may not be the best route to concurrency. I think Erlang demonstrates that concept. If that is the case, then perhaps native threads in Ruby are not the best way to achieve concurrency.