I was wondering if there was a way to execute very simple tasks on another thread in scala that does not have a lot of overhead?
Basically I would like to make a global ‘executor’ that can handle executing an arbitrary number of tasks. I can then use the executor to build up additional constructs.
Additionally it would be nice if blocking or non-blocking considerations did not have to be considered by the clients.
I know that the scala actors library is built on top of the Doug Lea FJ stuff, and also that they support to a limited degree what I am trying to accomplish. However from my understanding I will have to pre-allocate an ‘Actor Pool’ to accomplish.
I would like to avoid making a global thread pool for this, as from what I understand it is not all that good at fine grained parallelism.
Here is a simple example:
import concurrent.SyncVar
object SimpleExecutor {
import actors.Actor._
def exec[A](task: => A) : SyncVar[A] = {
//what goes here?
//This is what I currently have
val x = new concurrent.SyncVar[A]
//The overhead of making the actor appears to be a killer
actor {
x.set(task)
}
x
}
//Not really sure what to stick here
def execBlocker[A](task: => A) : SyncVar[A] = exec(task)
}
and now an example of using exec:
object Examples {
//Benchmarks a task
def benchmark(blk : => Unit) = {
val start = System.nanoTime
blk
System.nanoTime - start
}
//Benchmarks and compares 2 tasks
def cmp(a: => Any, b: => Any) = {
val at = benchmark(a)
val bt = benchmark(b)
println(at + " " + bt + " " +at.toDouble / bt)
}
//Simple example for simple non blocking comparison
import SimpleExecutor._
def paraAdd(hi: Int) = (0 until hi) map (i=>exec(i+5)) foreach (_.get)
def singAdd(hi: Int) = (0 until hi) foreach (i=>i+5)
//Simple example for the blocking performance
import Thread.sleep
def paraSle(hi : Int) = (0 until hi) map (i=>exec(sleep(i))) foreach (_.get)
def singSle(hi : Int) = (0 until hi) foreach (i=>sleep(i))
}
Finally to run the examples (might want to do it a few times so HotSpot can warm up):
import Examples._
cmp(paraAdd(10000), singAdd(10000))
cmp(paraSle(100), singSle(100))
That’s what
Futureswas made for. Justimport scala.actors.Futures._, usefutureto create new futures, methods likeawaitAllto wait on the results for a while,applyorrespondto block until the result is received,isSetto see if it’s ready or not, etc.You don’t need to create a thread pool either. Or, at least, not normally you don’t. Why do you think you do?
EDIT
You can’t gain performance parallelizing something as simple as an integer addition, because that’s even faster than a function call. Concurrency will only bring performance by avoiding time lost to blocking i/o and by using multiple CPU cores to execute tasks in parallel. In the latter case, the task must be computationally expensive enough to offset the cost of dividing the workload and merging the results.
One other reason to go for concurrency is to improve the responsiveness of the application. That’s not making it faster, that’s making it respond faster to the user, and one way of doing that is getting even relatively fast operations offloaded to another thread so that the threads handling what the user sees or does can be faster. But I digress.
There’s a serious problem with your code:
Or, translating into futures,
You might think
paraAddis doing the tasks in paralallel, but it isn’t, becauseRangehas a non-strict implementation ofmap(that’s up to Scala 2.7; starting with Scala 2.8.0,Rangeis strict). You can look it up on other Scala questions. What happens is this:0untilhifuture(i+5)when called.i => future(i+5)), the element is evaluated (foreachis strict) and then the functionapplyis called on it.So, because
futureis not called in step 2, but only in step 3, you’ll wait for eachfutureto complete before doing the next one. You can fix it with:Which will give you better performance, but never as good as a simple immediate addition. On the other hand, suppose you do this:
And then compare:
You may start seeing gains (it will depend on the number of cores and processor speed).