I recently bought the pre-print edition of the book Actors in Scala. At the moment the books is not yet complete; the last three chapters are missing and I found a number of minor mistakes, but it looks like this is going to be a useful book on actors.
To experiment with actors, I created a new hobby project: MandelActors. It’s a simple Mandelbrot fractal generator that uses Scala actors. MandelActors works as follows.
First of all, instead of simply mapping each pixel to one point on the complex plane and using that point in the Mandelbrot algorithm to compute what the color of the pixel should be, MandelActors uses a more sophisticated way to generate samples and integrate them into an image, which makes it possible to make more accurate and higher quality images. A sampler generates one or more samples for each pixel. Each sample is mapped to the complex plane and a color is computed using the Mandelbrot algorithm which is then integrated into the output image using a reconstruction filter. The sampling and reconstruction filter code is borrowed from another hobby project, ScalaRay, and the ideas come from the book Physically Based Rendering – From Theory to Implementation. The sampler and reconstruction filters are implemented in
The interesting things with regards to actors happen in the implementations of trait
Renderer.scala. I implemented four different renderers:
SingleThreadRenderer– This renderer simply renders the image on a single thread. This is useful as a baseline for performance comparisons.
EventActorsRenderer– Uses event-based actors.
ThreadActorsRenderer– Uses thread-based actors.
ThreadsRenderer– Uses threads instead of actors.
One of the things to be aware of when working with Scala actors is that there are two kinds of actors: event-based actors and thread-based actors. Most of the tutorials and descriptions of actors I found on the web don’t explain this distinction very well, which is strange, because it’s very important to understand the difference.
If you’ve read something about Scala actors, then you’ll most likely already know that there are two ways that an actor can receive messages: by using
receive or by using
When you use
receive, the actor will wait for a message to arrive that matches one of the cases in the block that you pass to
receive, blocking the thread that it’s running on. So in this model, each actor needs its own thread – these are thread-based actors. Thread-based actors are relatively heavyweight, because each thread takes up system resources (it needs its own stack frame, for example).
When you use
react, things work in a very different way. What this does is register the block that you pass it as an event handler. The actor system has a pool of threads that call the event handlers when messages arrive for the actors. Here actors are decoupled from threads – unlike with thread-based actors, there can be as few or as many threads as is optimal for the system, independent of the number of actors. Event-based actors are much more lightweight than thread-based actors – you can easily create tens or hundreds of thousands of actors without using up too many system resources.
EventActorsRenderer creates a separate actor for each batch of samples. Depending on the configuration, it might create up to a few thousand event-based actors.
ThreadActorsRenderer creates just a few thread-based actors, and sends batches of samples to them in a round-robin scheduling fashion.
If I’d have to choose, I’d go for event-based actors as my first choice, because they are simpler to program and you can leave things like scheduling completely in the hands of the actors system (which hopefully does it in a way that’s optimal for the hardware that you’re running the program on).
One thing I’d like to try out with MandelActors when I get the time for it is remote actors. (Unfortunately that’s in one of the chapters of Actors in Scala that’s still missing in the pre-print edition).
Besides the actors in Scala’s standard library, there are a number of other implementations of actors in Scala. Scalaz, Akka and Lift each have their own implementations. You can find a comparison of these together with standard Scala actors in this blogpost by Viktor Klang.
I don’t know why the various libraries have their own implementations of actors, but I did find this post on David Pollak’s motivations for moving away from the standard actors library. (When reading that, keep in mind that that post was written in May 2009, before Scala 2.8 – things have almost certainly changed since then). And here is a presentation about Scalaz actors.