Jan 14, 2011

Concurrent frameworks

Newer languages such as Scala re-introduced not as new concurrency models such as Actors. In a few recent posts people suggest different ways to compare frameworks available on the JVM.

I believe juxtaposing j.u.c and the rest of the frameworks mentioned by both authors is fundamentally wrong. Concurrency goes all the way down to real hardware (just think about CMPXCHG on x86 for example and its role in support of CAS on which virtually all non-blocking algorithms are based). Theoretically, there could be a different concurrency model implemented at that level but it is not the case in our commodity PC world. Even if there's bus-based message passing deep inside of your CPU you have no exposure to it, just barrier-like instructions.

Thus, everything starts from whatever hardware supports. If you implement a drastically different model on top of it you will have to pay a price. Which people who really care about concurrency will not be comfortable with. So you are likely to squeeze the most concurrency from a software approach which mirrors the hardware approach the most (think about all the Unsafe support Mr. Lea has from the JVM guys or read locks Azul guys have in their CPU to enable their miraculous pauseless garbage collector). 

It's only natural to consider everything else to be just additional levels of indirection in the concurrent stack. The core primitives exposed by your operating system will be quite close to the memory model of actual hardware. Then there's the tricky question of memory models in language runtime libraries, something which we on the JVM are lucky to have solved since JSR-133. All this complexity means that we are not likely to see anything comparable to j.u.c which is tightly integrated with the JVM, which in turn attempts via native means to get as close as possible to the hardware.

On the flip side, there are people who don't need every last drop of concurrency, are too young to be proficient  in even something as high-level as j.u.c or have access to unreasonable number of CPU cores and so can trade CPU cycles for code simplicity. The latter category is like to be increasingly dominant provided all the multi-core forecasts we have heard in the last few years are correct. And so Actors and similar models are extremely likely to be just j.u.c. queues and executors in disguise. 

It's good engineering to reuse higher-level components built on top of more challenging foundational blocks. Once we have enough CPU cores to waste very few people will probably care about old school concurrent primitives. But it will still be as good to be able to see j.u.c. behind some Actor facade as it is to see C code behind C++ abstractions or Java behind Scala. It allows you to better predict the amount of garbage and the performance penalty for using higher levels of abstraction.

On a related note,  I am not even entirely convinced we can separate F/J from j.u.c. proper. The former seems to be just a different set of trade-offs implemented within the same framework to solve a different class of problems. You want IO concurrency, you use the classical j.u.c. You want embarrassingly parallel fine-grained computations, you rely on F/J.