I have been maintaining grungy code, with lots of Java-esque stuff like this:
{
def value = 99
def s = 'string' + value
I have been ‘Groovifying’ the code into a (slightly) more readable version:
{
def value = 99
def s = "string$value"
(This micro-change makes more sense/value if you see the real code but that’s under wraps, so you’ll have to trust me on this…)
Anyway, I started wondering if I was really doing the right thing, performance-wise, so I knocked up the following sequence of tests:
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 70ms.
VisualVM lets me see how many strings are created. Not too many, as one would expect:
Here’s the ‘before’ case:
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def x = 'test' + i
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 292ms.
And the ‘after’ case:
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def x = "test$i"
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 368ms.
There’s clearly a lot more going on here, so it’s good that the run time is still “roughly in the same ballpark” as before.
Was I doing the right thing? Well, the system I am working on is really pretty small and this sort of micro-level concern constitutes about 0.000000000001% of the performance of the system. In terms of readability, I think I am winning, so I’m still happy.
For completeness, there’s a few other cases I hacked around on. None was particularly surprising:
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def x = "test$i".toString()
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 478ms.
This one is interesting because its pretty much a standard Java idiom:
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def x = String.format('test%d', i)
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 2583ms.
It’s also the slowest (and VisualVM showed a lot of pattern matching going on “behind the scenes”). Its probably worth remembering this when anyone talks about how dynamic languages are not performant in “The Real World.”
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def sb = new StringBuilder('test')
sb.append(i)
def x = sb.toString()
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 260ms.
def ts = System.currentTimeMillis()
for (i in 1..1000000) {
def sb = new StringBuilder('test')
sb < < i
def x = sb.toString()
}
println ("Took: ${System.currentTimeMillis() - ts}ms.")
// Took: 300ms.
As is my wont, I’m not going to draw any conclusions (there’s “lies, damn lies and performance statistics”, remember…or maybe “the beauty of performance statistics lies in the eye of the beholder”).
Hope you found all this interesting, however.
Curiosity may have killed the cat…it’ll probably give me RSI.
