# Loops performance in Groovy

## Introduction

In the 2018 Advent of Code challenged I solved all the puzzles in Groovy. It is pretty obvious, that choosing good data structure is the most important to obtain performant solution. However, the way we iterate over those structures is also very significant, at least when using Groovy.

## Measuring performance

I want to measure how long it takes to sum some numbers. For testing performance of loops I prepared a small function that simply sums some numbers:

void printAddingTime(String message, long to, Closure<Long> adder) { LocalTime start = LocalTime.now() long sum = adder(to) println("$message: $sum calculated in ${Duration.between(start, LocalTime.now()).toMillis()} ms") }

Pseudo code for summing functions is below:

for i = 1 to n for j = 1 to n sum += i * j end end

## Loops types

Let’s implement the summing function in various ways.

`collect`

and `sum`

First loop type is to use built-in (by Groovy) function `collect`

and `sum`

on collections (`Range`

it this example):

(1..n).collect { long i -> (1..n).collect { long j -> i * j }.sum() }.sum()

`each`

Next, let’s write the same function using `each`

built-in function on collections (`Range`

it this example) and then add results to accumulator variable:

long sum = 0 (1..n).each { long i -> (1..n).each { long j -> sum += i * j } } return sum

`times`

Now instead of using `each`

we could use the function `times`

built-in on `Number`

by Groovy:

long sum = 0 n.times { long i -> n.times { long j -> sum += (i + 1)*(j+1) } } return sum

We have to add `1`

to `i`

and `j`

because times generates numbers from `0`

to `n`

exclusive.

`LongStream`

with `sum`

Java 8 came with a new feature – streams. One example of streams is `LongStream`

. Fortunately, it has `sum`

built-in function, which we can use:

LongStream.range(0, n).map { i -> LongStream.range(0, n).map { j -> (i + 1) * (j + 1) }.sum() }.sum()

`LongStream`

generates numbers in the same way as `times`

function, so we also have to add `1`

to `i`

and `j`

here.

`LongStream`

with manual sum

Instead of `sum`

function on `LongStream`

, we can add all numbers manually:

long sum = 0 LongStream.range(0, n).forEach { i -> LongStream.range(0, n).forEach { j -> sum += (i + 1) * (j + 1) } } return sum

`while`

Of course since Groovy inherits from Java a big part of its syntax, we can use the `while`

loop:

long sum = 0 long i = 1 while(i <= n){ long j = 1 while(j <= n){ sum+= i*j ++j } ++i } return sum

`for`

As we can use `while`

, we can also use `for`

loop in Groovy:

long sum = 0 for (long i = 1; i <= n; ++i) { for (long j = 1; j <= n; ++j) { sum += i * j } } return sum

## Results

My tests I run on Java `1.8`

and Groovy `2.5.5`

. Script `loops.groovy`

was fired using bash script:

#!/bin/sh for x in 10 100 1000 10000 100000; do echo $x groovy loops.groovy $x echo done

Values are in milliseconds

Loop n | 10 | 100 | 1000 | 10000 | 100000 |
---|---|---|---|---|---|

`collect` + `sum` | 7 | 22 | 216 | 16244 | 1546822 |

`each` | 12 | 17 | 118 | 7332 | 706781 |

`times` | 2 | 10 | 109 | 8264 | 708684 |

`LongStream` + `sum` | 7 | 17 | 127 | 7679 | 763341 |

`LongStream` + manual sum | 18 | 35 | 149 | 6857 | 680804 |

`while` | 8 | 20 | 103 | 3166 | 301967 |

`for` | 7 | 10 | 25 | 359 | 27966 |

As you can spot, for small amount of iterations using built-in Groovy functions is good enough, but for much bigger amount of iterations we should use `while`

or `for`

loops like in plain, old Java.

## Show me the code

Code for those examples are available here. You can run those examples on your machine and check performance on your own.

Published on Java Code Geeks with permission by Dominik Przybysz, partner at our JCG program. See the original article here: Loops performance in Groovy Opinions expressed by Java Code Geeks contributors are their own. |