Sunday, December 1, 2013

Groovy Parallel and Async Tasks with GPars

Groovy is a very pragmatic language. It keeps ceremony to the minimum while taking advantage of the mighty JVM and the Java platform. The GPars library is a convenient way to run computations in parallel, async or concurrent. The same thing can be achieved to an extend with plain Java with the java.util.concurrent.* classes. An article by Edgardo Hernandez demonstrates the usage with Java. Let's use Groovy and GPars to do something similar.
import groovyx.gpars.GParsPool

public class Task {
def compute(int msg) {
String str = "";
long begTest = new java.util.Date().getTime();
System.out.println("start - Task "+ msg);
try {
for(int i = 0; i < 20000; i++)
str = str + 't';
} catch (InterruptedException e) {}
Double secs = new Double((new java.util.Date().getTime() - begTest)*0.001);
System.out.println("run time " + secs + " secs");
return msg;

final def task = new Task()
def seq = {
println "sequential"
long b = new Date().getTime()
(1..5).each {
long e = new Date().getTime()
println e-b

println "\nparallel"
GParsPool.withPool { //pool size will be automatically computed (if nothing is specified).
long b = new Date().getTime()
(1..5).eachParallel { // executes each block in parallel and statement within the block sequentially.
long e = new Date().getTime()
println e-b

println "\nasync (demo)"
long start = new Date().getTime()
// Say we have few tasks that needs to be computed asynchronously while doing other computations.
def a = {task.compute(1)} // define the tasks in a block
def b = {task.compute(2)}
def c = {task.compute(3)}
def d = {task.compute(4)}
def e = {task.compute(5)}
def results = GParsPool.withPool {
[a,b,c,d,e]*.callAsync() // perform the tasks in the background
// continue with other tasks..
results*.get() // now we need the results to continue further. Get the result (if not done yet, it waits till done).
// do further computation with the obtained results..
long stop = new Date().getTime()
println stop-start
This can be run using groovyConsole. GPars must be in the classpath or included using grapes. In my test system having dual cores, the time taken are around 10375ms, 6578ms, 6234ms for sequential, parallel, and async tasks respectively. In a single core atom processor the average time for 3 runs are as 13792ms, 10771ms and 9078ms respectively.