“The purpose of computing is insight, not numbers.”
Richard Hamming
A fundamental assumption of software developers is that the system libraries are error-free and perform well. Every now and then you are taught better, but after a while the memories fade and you hang on to the original misconceptions.
During the performance measurements of the FreshMarker library, it became apparent that the naive use of the DecimalFormat
class results in a considerable loss of speed.
For this reason, the c
Built-In for numbers is currently used in the benchmarks for FreshMarker. Other template engines do not use any formatter in the benchmarks, so this is not really a trick.
The following table lists some benchmarks with JMH (Java Microbenchmark Harness) whose values differ greatly from one another.
Benchmark | ops/s |
DecimalFormat long | 3.677.384,716 |
DecimalFormat double | 15.102.242,841 |
Formatter long | 7.555.020,987 |
String.valueOf long | 42.179.552,006 |
NumberFormatter long | 11.475.252,138 |
NumberFormatter double | 12.558.752,656 |
The first test uses the DecimalFormat#format(long)
method. Similar values are also obtained for DecimalFormat#format(Object)
. Compared to the other benchmarks, the performance is extremely poor. The second benchmark has a much better performance because the DecimalFormat#format(double)
method used there contains an optimization called fast-path.
The Formatter Benchmark uses the Formatter
, which has been included in the standard library since Java 5. It is significantly faster for long
values but significantly slower than DecimalFormat
for double
values.
Of course, longs
can also be formatted faster if you do not localize the values. This is shown by the third benchmark of the String#valueOf(long)
method. Unfortunately, there is no variant of this method with a localization.
The last two lines show the performance of the next FreshMarker NumberFormatter
. This formatter is a wrapper around DecimalFormat
that encapsulates the details of the localized formatting of numbers for FreshMarker. Its performance is slightly below that of the DecimalFormat
performance for double
. Both for double
and for long
values. Some of you will have guessed that the good performance for long
values is achieved by converting them to double
values.
In the future, I will probably have to adapt the localized formatting of long
values to the implementation of String#valueOf(long)
so that the performance of the template engine is noticeably better at this point.
For all readers, however, it is worth knowing about the performance of the libraries used, even without the use of a template engine.