Tuesday, January 5, 2010

Another observation on Groovy's speed

Not specifically about CouchDB, but have been experimenting with
REST-based Java/Groovy DAOs using CouchDB as the backend. Thought folks
here might be interested in the info.

I created some REST DAOs using Groovy's HttpBuilder, and Spring's
RestTemplate. Both use Google's GSON library for serializing to JSON.

Examples of create method:

Spring's RestTemplate implementation:


public String create(Pojo pojo) {
Map response = restTemplate.postForObject(url, pojo, Map.class);
String id = (String)response.get("id");
String rev = (String)response.get("rev");
pojo.setKey(id);
pojo.setRevision(rev);

return pojo.getKey();
}

Groovy's HttpBuilder implementation:

String create(Pojo pojo) {
httpBuilder.request(POST, JSON) {
send JSON, toJson(pojo)
response.success = {resp, json ->
pojo.key = json.id as String
pojo.revision = json.rev as String
}
}
pojo.key
}

The Spring implementation is nearly 50 times faster than the Groovy implementation!

Of course, the Groovy implementation was much easier to learn and implement the Couch API with and was great for prototyping.

Scala vs Groovy performance

Not an official performance comparison, just some observations made in the past week or two.

Quick port of some old Java graphics code I wrote into both Groovy and Scala scripts.
Execution time for main loop:

Groovy: 20.319 seconds
Scala: 0.335 seconds

I know this is like comparing apples and oranges, but still... 60 times slower?

I'm pretty turned-off from considering Groovy for anything other than quick prototyping or non-application scripting.

Code below (not meant to be high-quality examples of either language):

Scala:


import java.awt.image.BufferedImage
import java.awt.image.BufferedImage.TYPE_INT_ARGB
import java.io.File
import javax.imageio.ImageIO.{read, write}

val file = args(0)
val input = read(new File(file))

val w = (input.getWidth / 2).asInstanceOf[Int] * 2
val h = input getHeight
var output = new BufferedImage(w / 2, h / 2, TYPE_INT_ARGB)
var r: Int = _
var g: Int = _
var b: Int = _
var lines = new Array[Array[Int]](w, 4)

val time = System.currentTimeMillis
println("Output: " + file.replace(".", "-clear_") + ".png")

(0 until (h - 1, 2)) foreach { y =>
(0 until w) foreach { x =>
val rgb = input getRGB (x, y)
val rgb2 = input getRGB (x, y + 1)

lines(x)(0) = (((rgb & 0xFF0000) >> 16) + ((rgb2 & 0xFF0000) >> 16)) / 2
lines(x)(1) = (((rgb & 0x00FF00) >> 8) + ((rgb2 & 0x00FF00) >> 8)) / 2
lines(x)(2) = ((rgb & 0x0000FF) + (rgb2 & 0x0000FF)) / 2
lines(x)(3) = (((rgb >> 24) & 0xFF) + ((rgb2 >> 24) & 0xFF)) / 2

if (x >= 2 && x % 2 == 0) {
r = lines(x - 2)(0)
g = lines(x - 1)(1)
b = lines(x)(2)

val a = (lines(x)(3) + lines(x - 1)(3) + lines(x - 2)(3)) / 3
val rgb3 = (a << 24) | (r << 16) | (g << 8) | b
output setRGB ((x / 2), (y / 2), rgb3)
}
}
}
println (System.currentTimeMillis - time)

write(output, "PNG", new File(file.replace(".", "-clear_") + ".png"))

Groovy:


import java.awt.image.BufferedImage
import static java.awt.image.BufferedImage.TYPE_INT_ARGB
import javax.imageio.ImageIO

def file = args[0]

println "Input: ${file}"

def BufferedImage input = ImageIO.read(new File(file))
def int w = (input.width / 2 as int) * 2
def int h = input.height
def BufferedImage output = new BufferedImage(w / 2 as int, h / 2 as int, TYPE_INT_ARGB)
def int r, g, b
def int[][] lines = new int[w][4]

println "Output: ${file.replace(".", "-clear_")}.png"

def time = System.currentTimeMillis()

for (int y = 0; y < h - 1; y += 2) {
(0..<w).each { int x ->
def int rgb = input.getRGB(x, y)
def int rgb2 = input.getRGB(x, y + 1)

lines[x][0] = (((rgb & 0xFF0000) >> 16) + ((rgb2 & 0xFF0000) >> 16)) / 2
lines[x][1] = (((rgb & 0x00FF00) >> 8) + ((rgb2 & 0x00FF00) >> 8)) / 2
lines[x][2] = ((rgb & 0x0000FF) + (rgb2 & 0x0000FF)) / 2
lines[x][3] = (((rgb >> 24) & 0xFF) + ((rgb2 >> 24) & 0xFF)) / 2

if (x >= 2 && x % 2 == 0) {
r = lines[x - 2][0]
g = lines[x - 1][1]
b = lines[x][2]

int a = (lines[x][3] + lines[x - 1][3] + lines[x - 2][3]) / 3
int rgb3 = (a << 24) | (r << 16) | (g << 8) | b
output.setRGB((x / 2) as int, (y / 2) as int, rgb3)
}
}
}
println(System.currentTimeMillis() - time)

ImageIO.write output, "PNG", new File("${file.replace(".", "-clear_")}.png")