RFR: 8327791: Optimization for new BigDecimal(String) [v18]

Shaojin Wen duke at openjdk.org
Sat Apr 27 10:51:10 UTC 2024


On Fri, 26 Apr 2024 05:48:03 GMT, Shaojin Wen <duke at openjdk.org> wrote:

>> The current BigDecimal(String) constructor calls String#toCharArray, which has a memory allocation.
>> 
>> 
>> public BigDecimal(String val) {
>>     this(val.toCharArray(), 0, val.length()); // allocate char[]
>> }
>> 
>> 
>> When the length is greater than 18, create a char[]
>> 
>> 
>> boolean isCompact = (len <= MAX_COMPACT_DIGITS); // 18
>> if (!isCompact) {
>>     // ...
>> } else {
>>     char[] coeff = new char[len]; // allocate char[]
>>     // ...
>> }
>> 
>> 
>> This PR eliminates the two memory allocations mentioned above, resulting in an approximate 60% increase in performance..
>
> Shaojin Wen has updated the pull request with a new target base due to a merge or a rebase. The incremental webrev excludes the unrelated changes brought in by the merge/rebase. The pull request contains 22 additional commits since the last revision:
> 
>  - Merge remote-tracking branch 'upstream/master' into optim_dec_new
>  - Merge remote-tracking branch 'upstream/master' into optim_dec_new
>  - use while instead for
>  - Update src/java.base/share/classes/java/math/BigDecimal.java
>    
>    Co-authored-by: Claes Redestad <claes.redestad at oracle.com>
>  - bug fix for CharArraySequence#charAt
>  - bug fix for CharArraySequence
>  - fix benchmark
>  - one CharArraySequence
>  - restore comment
>  - easier to compare
>  - ... and 12 more: https://git.openjdk.org/jdk/compare/26169c54...bb620ba3

In money-related scenarios, such as product prices, etc., BigDecimal needs to be used instead of float/double. I focus on optimizing the performance of BigDecimal's construction and toString scenarios.

Constructing BigDecimal usually includes two scenarios:
* new BigDecimal(char[], int, int), Many libraries, such as mysql drveri/fastjson/jackson and other commonly used libraries use this function to construct BigDecimal
* new BigDecimal(String), There are also many scenarios, such as data integration scenarios, scenarios where json is read from the message queue and then converted into target database row records, often using new BigDecimal(String)

I did a performance comparison test on Mac Book M1 Pro. The compared branches are:
* [baseline](https://github.com/wenshao/jdk/tree/upstream_master_0312) https://github.com/wenshao/jdk/tree/upstream_master_0312
* bb620ba  [Full](https://webrevs.openjdk.org/?repo=jdk&pr=18177&range=17) - [Incremental](https://webrevs.openjdk.org/?repo=jdk&pr=18177&range=16-17) ([bb620ba3](https://git.openjdk.org/jdk/pull/18177/files/bb620ba39a6f1ce17ff273bac54ebb709beb1667))


# 1. new BigDecimal(String) benchmark
Execute the following commands respectively 

make test TEST="micro:java.math.BigDecimals.testConstructorWithHugeString"
make test TEST="micro:java.math.BigDecimals.testConstructorWithLargeString"
make test TEST="micro:java.math.BigDecimals.testConstructorWithSmallString"
make test TEST="micro:java.math.BigDecimals.testConstructorWithString"


## benchmark result

-Benchmark                                      Mode  Cnt    Score   Error  Units #baseline
-BigDecimals.testConstructorWithHugeString      avgt   15  112.994 ? 2.342  ns/op
-BigDecimals.testConstructorWithLargeString     avgt   15  114.016 ? 2.529  ns/op
-BigDecimals.testConstructorWithSmallString     avgt   15   19.526 ? 0.078  ns/op
-BigDecimals.testConstructorWithString          avgt   15   68.058 ? 0.836  ns/op

+Benchmark                                      Mode  Cnt    Score    Error  Units #bb620ba
+BigDecimals.testConstructorWithHugeString      avgt   15   96.838 ?  8.743  ns/op +16.68%
+BigDecimals.testConstructorWithLargeString     avgt   15   20.904 ?  0.112  ns/op +445.42%
+BigDecimals.testConstructorWithSmallString     avgt   15   16.083 ?  0.042  ns/op +21.40%
+BigDecimals.testConstructorWithString          avgt   15   35.912 ?  0.350  ns/op +85.91%


It can be seen that there is a significant performance improvement in the new BigDecimal(String) scenario.

| case |  |
|-|-|
|BigDecimals.testConstructorWithHugeString |16.68% |
|BigDecimals.testConstructorWithLargeString |445.42% |
|BigDecimals.testConstructorWithSmallString |21.40% |
|BigDecimals.testConstructorWithString |85.91% |

# 2. new BigDecima(char[],int,int) benchmark

Execute the following commands respectively 

make test TEST="micro:java.math.BigDecimals.testConstructorWithHugeCharArray"
make test TEST="micro:java.math.BigDecimals.testConstructorWithLargeCharArray"
make test TEST="micro:java.math.BigDecimals.testConstructorWithSmallCharArray"
make test TEST="micro:java.math.BigDecimals.testConstructorWithCharArray"


## benchmark result

-Benchmark                                      Mode  Cnt    Score   Error  Units #baseline
-BigDecimals.testConstructorWithHugeCharArray   avgt   15   94.554 ? 4.262  ns/op
-BigDecimals.testConstructorWithLargeCharArray  avgt   15   94.669 ? 3.065  ns/op
-BigDecimals.testConstructorWithSmallCharArray  avgt   15   16.457 ? 0.081  ns/op
-BigDecimals.testConstructorWithCharArray       avgt   15   47.555 ? 0.562  ns/op

+Benchmark                                      Mode  Cnt    Score    Error  Units #bb620ba
+BigDecimals.testConstructorWithHugeCharArray   avgt   15  113.113 ?  1.924  ns/op -16.40%
+BigDecimals.testConstructorWithLargeCharArray  avgt   15   24.150 ?  0.098  ns/op +292.00%
+BigDecimals.testConstructorWithSmallCharArray  avgt   15   19.524 ?  0.044  ns/op -15.70%
+BigDecimals.testConstructorWithCharArray       avgt   15   52.178 ? 14.289  ns/op -8.86%


The current implementation has significant performance improvements in testConstructorWithLargeCharArray, but has performance degradation in the other four scenarios. The performance degradation of some of these scenarios is due to the need to avoid code duplication. If we don't care about code duplication, we can improve performance in all scenarios.

| case |  |
|-|-|
|BigDecimals.testConstructorWithHugeCharArray |-16.40% |
|BigDecimals.testConstructorWithLargeCharArray |+292.00% |
|BigDecimals.testConstructorWithSmallCharArray |-15.70% |
|BigDecimals.testConstructorWithCharArray |-8.86% |

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

PR Comment: https://git.openjdk.org/jdk/pull/18177#issuecomment-2080449446


More information about the core-libs-dev mailing list