Replacing == ZERO by isZero() on BigDecimal class.
Motivation
equals method calls BD.compare(BD) which requires to bring the significant to same exponent before comparing (if either exponent or precision are not equals). This calculation go through getRidOfRadix and numberOfDecimalDigits which is in total quite expansive.
Metrics
As I've seen in #299 and #300, optimization gains are relatively similar no matter the platform. To save some time I'll only use JVM (the quickest) to benchmark all public methods directly involving the '== ZERO'. (Don't hesitate to ask me for more benchmarks if you think it's risky.)
Benchmark (JVM)
Best of 3 runs, 1000000 iterations each, M3 Pro with 36GB RAM, duration in ms
Method
BEFORE
AFTER
% time reduction
subtract
395
219
44%
add
287
164
43%
pow
269
153
43%
hashCode
202
66
67%
add(ZERO)
79
20
75%
toStringExpanded
167
110
34%
Benchmark code
@Test
fun perfAddZero() {
val bd = BigDecimal.parseString("123")
val duration = measureTime {
repeat(1_000_000) { bd + BigDecimal.ZERO }
}
println("add(ZERO): $duration")
}
@Test
fun perfAdd() {
val a = BigDecimal.parseString("123")
val b = BigDecimal.parseString("34.56")
val duration = measureTime {
repeat(1_000_000) { a + b }
}
println("add: $duration")
}
@Test
fun perfSubtract() {
val a = BigDecimal.parseString("123")
val b = BigDecimal.parseString("34.56")
val duration = measureTime {
repeat(1_000_000) { a - b }
}
println("subtract: $duration")
}
@Test
fun perfHashCode() {
val bd = BigDecimal.parseString("123.45")
val duration = measureTime {
// hashCode uses removeTrailingZeroes
repeat(1_000_000) { bd.hashCode() }
}
println("hashCode: $duration")
}
@Test
fun perfPow() {
val bd = BigDecimal.parseString("123.45")
val duration = measureTime {
repeat(1_000_000) { bd.pow(3) }
}
println("pow: $duration")
}
@Test
fun perfToStringExpanded() {
val bd = BigDecimal.parseString("123.45")
val duration = measureTime {
repeat(1_000_000) { bd.toStringExpanded() }
}
println("toStringExpanded: $duration")
}
Replacing
== ZERO
byisZero()
on BigDecimal class.Motivation
equals
method callsBD.compare(BD)
which requires to bring the significant to same exponent before comparing (if either exponent or precision are not equals). This calculation go through getRidOfRadix and numberOfDecimalDigits which is in total quite expansive.Metrics
As I've seen in #299 and #300, optimization gains are relatively similar no matter the platform. To save some time I'll only use JVM (the quickest) to benchmark all public methods directly involving the '== ZERO'. (Don't hesitate to ask me for more benchmarks if you think it's risky.)
Benchmark (JVM)
Best of 3 runs, 1000000 iterations each, M3 Pro with 36GB RAM, duration in ms
Benchmark code
Raw data
Before
Run 1 subtract: 395.261291ms add: 288.861958ms pow: 282.718375ms hashCode: 212.823917ms add(ZERO): 93.253209ms toStringExpanded: 167.068250ms
Run 2 subtract: 397.731ms add: 312.411208ms pow: 269.341042ms hashCode: 206.681333ms add(ZERO): 78.573125ms toStringExpanded: 167.801541ms
Run 3 subtract: 394.628875ms add: 286.920209ms pow: 279.920042ms hashCode: 202.090084ms add(ZERO): 79.048292ms toStringExpanded: 176.490833ms
After
Run 1 subtract: 225.815875ms add: 163.852750ms pow: 155.257792ms hashCode: 67.060416ms add(ZERO): 20.868541ms toStringExpanded: 117.800792ms
Run 2 subtract: 229.606292ms add: 196.132167ms pow: 153.424542ms hashCode: 66.612959ms add(ZERO): 19.638667ms toStringExpanded: 109.715250ms
Run 3 subtract: 219.301333ms add: 187.846375ms pow: 157.756ms hashCode: 66.152625ms add(ZERO): 20.008958ms toStringExpanded: 111.473625ms