## 25 July, 2011

### java.math.BigDecimal performance

These days I had to do some monetary calculations in Java. This reminded me again about primitive `double` type and that it is absolutely the worst thing you can use when dealing with money. As you know, primitive `double` don't necessarily give you the right value, only the value that can be stored in a binary number format (any decimal number is represented by an approximated value). Whilst, `java.math.BigDecimal` gives you the required precision, its performance is very poor comparable to primitive types.

Below are few simple tests that are measuring `BigDecimal` performance based on a simple computation `x2 + y2` which is performed in a loop 250000 times. In order to exclude the JVM warm-up cost, each test was executed 10 times. The execution time was calculated as average of all executions except first execution `(exec2 + exec3 + … + exec10) / 9`.

1. Test `BigDecimal` computations (execution average time: ~ 767 ms)
```BigDecimal bd = null;
for (double x = 0.5; x <= 500; x += 0.5) {
for (double y = 0.5; y <= 500; y += 0.5) {
}
}
```
We got around 0.003 ms per computation. Not bad, but wait to see how fast primitive `double` is. Next test will show us how much time we spend on creation of `BigDecimal` objects.
2. Test `BigDecimal` constructions (execution average time: ~ 468 ms)
```for (double x = 0.5; x <= 500; x += 0.5) {
for (double y = 0.5; y <= 500; y += 0.5) {
BigDecimal.valueOf(x);
BigDecimal.valueOf(y);
}
}
```
As you can see, only construction of initial operands (`x` and `y`) took around 61% of computation time. We can assume that rest 39% are also spent on `BigDecimal` object creations (results of `pow()` and `add()` operations). Knowing that `BigDecimal` objects are immutable, let’s see how much we can gain by caching `BigDecimal` instances.
3. Test `BigDecimal` computations with cache (execution average time: ~ 309 ms)
```BigDecimal bd = null;
for (double x = 0.5; x <= 500; x += 0.5) {
for (double y = 0.5; y <= 500; y += 0.5) {
BigDecimal bigDecimal1 = cache.get(x);
BigDecimal bigDecimal2 = cache.get(y);
}
}
```
Cache was prepared as follows:
```Map<Double, BigDecimal> cache = new HashMap<Double, BigDecimal>();
for (double x = 0.5; x <= 500; x += 0.5) {
cache.put(x, BigDecimal.valueOf(x));
}
```
As you can see, caching `BigDecimal` gives a performance boost. Now it’s time to see how the primitive `double` type is performing.
4. Test `double` computations (execution average time: ~ 2 ms)
```double d = 0.0;
for (double x = 0.5; x <= 500; x += 0.5) {
for (double y = 0.5; y <= 500; y += 0.5) {
d = x * x + y * y;
}
}
```
Computation speed is great but unfortunately floating point numbers cannot be used for monetary calculations.

Performance Improvement to `BigDecimal`

Java SE 6 Update 25 made some performance improvements for `BigDecimal`. Following is the excerpt from Java SE 6 Update 25 Release Notes:

Improvements have been made to class BigDecimal enhancing its performance by thirty percent. BigDecimal is enabled by specifying `-XX:+AggressiveOpts` command option.

I repeated all the above tests with `-XX:+AggressiveOpts` option. Below is the table with results:

 Test Average Exec Time Performance Boost Normal -XX:+AggressiveOpts `BigDecimal` computations 767 ms 644 ms 16% `BigDecimal` constructions 468 ms 394 ms 16% `BigDecimal` computations with cache 309 ms 309 ms 0% `double` computations 2 ms - -

Results speak themselves, `-XX:+AggressiveOpts` option gave us 16% performance improvement. It’s not a 30% improvement but still it is something, if we take into account that we just added a JVM option. The most interesting thing is that we got 0% performance improvement for the test which uses cache and the performance improvement of `BigDecimal` computations test and `BigDecimal` constructions test are the same. This leads me to think that improvements were made mostly for `BigDecimal` objects creation.

Regarding `BigDecimal` I would like also to remind you about:

• Never use `BigDecimal(double)` constructor. The recommended constructor is `BigDecimal(String)`, or in case you want to convert an existing double into a `BigDecimal` you can use `BigDecimal.valueOf(double)` static method.
• When dividing always specify a `RoundingMode`. Instead of `divide(BigDecimal)` use `divide(BigDecimal, RoundingMode)`. This has to be done in order to avoid `ArithmeticException`. Below is an excerpt from `BigDecimal` javadoc:
In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an `ArithmeticException` is thrown.

At the end, I want to say that for my task I used neither `double` nor `BigDecimal`. I used primitive `long` type and I stored money as cents, pennies (or the equivalent, of course). The choice was done because of performance reasons. I had to do many very simple computations but very often.

Update (05-Aug-2011): Be aware of bug, recently discovered in Java 7 which is also applicable for Java 6 in case when the following JVM options are used: `-XX:+OptimizeStringConcat` or `-XX:+AggressiveOpts`. More information here: Java7 Hotspot Loop Bug Details.

Update (01-Mar-2012): Bug was fixed in Java 6 update 29 and Java 7 update 1.