<i18n dev> RFR: JDK-8282625 Formatter caches Locale/DecimalFormatSymbols poorly [v3]

Jaikiran Pai jpai at openjdk.java.net
Sat Mar 5 14:23:59 UTC 2022


On Fri, 4 Mar 2022 21:17:50 GMT, Jim Laskey <jlaskey at openjdk.org> wrote:

>> Several attempts have been made to improve Formatter's numeric performance by caching the current Locale zero. Such fixes, however, ignore the real issue, which is the slowness of fetching DecimalFormatSymbols. By directly caching DecimalFormatSymbols in the Formatter, this enhancement streamlines the process of accessing Locale DecimalFormatSymbols and specifically getZeroDigit(). The result is a general improvement in the performance of numeric formatting. 
>> 
>> 
>> @Benchmark 
>> public void bigDecimalDefaultLocale() { 
>>     result = String.format("%1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f", X); 
>> } 
>> 
>> @Benchmark 
>> public void bigDecimalLocaleAlternate() { 
>>     result = String.format(THAI, "%1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f", X); 
>>     other = String.format(DEFAULT, "%1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f", X); 
>> } 
>> 
>> @Benchmark 
>> public void bigDecimalThaiLocale() { 
>>     result = String.format(THAI, "%1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f %1$f", X); 
>> } 
>> 
>> @Benchmark 
>> public void integerDefaultLocale() { 
>>     result = String.format("%1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d", x); 
>> } 
>> 
>> @Benchmark 
>> public void integerLocaleAlternate() { 
>>     result = String.format(THAI, "%1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d", x); 
>>     other = String.format(DEFAULT, "%1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d", x); 
>> } 
>> 
>> @Benchmark 
>> public void integerThaiLocale() { 
>>     result = String.format(THAI, "%1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d %1$d", x); 
>> } 
>> 
>> 
>> Before: 
>> Benchmark Mode Cnt Score Error Units 
>> MyBenchmark.bigDecimalDefaultLocale thrpt 25 75498.923 ± 3686.966 ops/s 
>> MyBenchmark.bigDecimalLocaleAlternate thrpt 25 39068.721 ± 162.983 ops/s 
>> MyBenchmark.bigDecimalThaiLocale thrpt 25 77256.530 ± 294.743 ops/s 
>> MyBenchmark.integerDefaultLocale thrpt 25 344093.071 ± 6189.002 ops/s 
>> MyBenchmark.integerLocaleAlternate thrpt 25 165685.488 ± 440.857 ops/s 
>> MyBenchmark.integerThaiLocale thrpt 25 327461.302 ± 1168.243 ops/s 
>> 
>> After: 
>> Benchmark Mode Cnt Score Error Units 
>> MyBenchmark.bigDecimalDefaultLocale thrpt 25 94735.293 ± 674.587 ops/s 
>> MyBenchmark.bigDecimalLocaleAlternate thrpt 25 44215.547 ± 291.664 ops/s 
>> MyBenchmark.bigDecimalThaiLocale thrpt 25 91390.997 ± 658.677 ops/s 
>> MyBenchmark.integerDefaultLocale thrpt 25 363722.977 ± 2864.554 ops/s 
>> MyBenchmark.integerLocaleAlternate thrpt 25 165789.514 ± 779.656 ops/s 
>> MyBenchmark.integerThaiLocale thrpt 25 351400.818 ± 1030.246 ops/s
>
> Jim Laskey has updated the pull request incrementally with one additional commit since the last revision:
> 
>   Too many 'the'

src/java.base/share/classes/java/util/Formatter.java line 2012:

> 2010: public final class Formatter implements Closeable, Flushable {
> 2011:     // Caching DecimalFormatSymbols
> 2012:     static DecimalFormatSymbols DFS = null;

Hello Jim,
The javadoc of `Formatter` states:

>
> Formatters are not necessarily safe for multithreaded access.  Thread safety is optional and is the responsibility of users of methods in this class.
>

So I would think that user applications would typically be synchronizing on the instance of a `Formatter` for any multi-threaded use of a formatter instance.

If I'm reading this changed code correctly, it's now possible that 2 separate instances of a `Formatter` being concurrently accessed (i.e. in different threads) will now try and update/use this cached class level `static` state `DFS`. That would thus require some kind of thread safety semantics to be implemented for this new `getDecimalFormatSymbols(Locale)` method, isn't it?

-------------

PR: https://git.openjdk.java.net/jdk/pull/7703


More information about the i18n-dev mailing list