pgjdbc/pgjdbc GitHub issues and pull requests (mirror)
help / color / mirror / Atom feed[pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost
4+ messages / 3 participants
[nested] [flat]
* [pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost
@ 2025-11-13 16:40 "lukaseder (@lukaseder)" <[email protected]>
0 siblings, 0 replies; 4+ messages in thread
From: lukaseder (@lukaseder) @ 2025-11-13 16:40 UTC (permalink / raw)
To: pgjdbc/pgjdbc <[email protected]>
**Describe the issue**
`PGInterval::getValue` uses `Stirng::format` and `DecimalFormat` to format the interval value to the PostgreSQL notation:
```java
public @Nullable String getValue() {
if (isNull) {
return null;
}
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.US);
df.applyPattern("0.0#####");
return String.format(
Locale.ROOT,
"%d years %d mons %d days %d hours %d mins %s secs",
years,
months,
days,
hours,
minutes,
df.format(getSeconds())
);
}
```
This performs terribly and should be avoided. I'm suggesting this alternative implementation, which should do the same thing (except it always renders trailing zeros):
```java
public String getValue() {
if (isNull) {
return null;
}
StringBuilder sb = new StringBuilder();
sb.append(years).append(" years ")
.append(months).append(" mons ")
.append(days).append(" days ")
.append(hours).append(" hours ")
.append(minutes).append(" mins ");
if (wholeSeconds < 0 || microSeconds < 0)
sb.append('-');
sb.append(Math.abs(wholeSeconds));
if (microSeconds != 0) {
sb.append('.');
String s = "" + Math.abs(microSeconds);
for (int i = s.length(); i < 6; i++)
sb.append('0');
sb.append(s);
}
sb.append(" secs");
return sb.toString();
}
```
**Driver Version?**
42.7.8
**Java Version?**
openjdk version "21.0.7" 2025-04-15 LTS
**OS Version?**
Microsoft Windows [Version 10.0.26100.7171]
**PostgreSQL Version?**
N/A
**To Reproduce**
Run this JMH benchmark:
```java
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
@Fork(value = 3)
@Warmup(iterations = 3, time = 3)
@Measurement(iterations = 5, time = 3)
public class PGIntervalBenchmark {
@State(Scope.Benchmark)
public static class BenchmarkState {
PGInterval interval;
@Setup(Level.Trial)
public void setup() throws Exception {
interval = new PGInterval(0, 0, 0, 0, 0, 0);
}
@TearDown(Level.Trial)
public void teardown() throws Exception {
interval = null;
}
}
@Benchmark
public String testGetValue(BenchmarkState state) {
return state.interval.getValue();
}
}
```
**Expected behaviour**
The current version produces these benchmark results:
```
PGIntervalBenchmark.testGetValue thrpt 15 1447069.902 ± 67832.795 ops/s
```
My version produces a 32x improvement:
```
PGIntervalBenchmark.testGetValue thrpt 15 46957485.917 ± 497298.690 ops/s
```
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: [pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost
@ 2025-11-13 16:56 ` "davecramer (@davecramer)" <[email protected]>
2 siblings, 0 replies; 4+ messages in thread
From: davecramer (@davecramer) @ 2025-11-13 16:56 UTC (permalink / raw)
To: pgjdbc/pgjdbc <[email protected]>
Well the except part is kinda important. It will probably break something
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: [pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost
@ 2025-11-13 17:04 ` "vlsi (@vlsi)" <[email protected]>
2 siblings, 0 replies; 4+ messages in thread
From: vlsi (@vlsi) @ 2025-11-13 17:04 UTC (permalink / raw)
To: pgjdbc/pgjdbc <[email protected]>
I agree there's no point in using `String.format` on the hot path.
^ permalink raw reply [nested|flat] 4+ messages in thread
* Re: [pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost
@ 2025-11-14 08:08 ` "lukaseder (@lukaseder)" <[email protected]>
2 siblings, 0 replies; 4+ messages in thread
From: lukaseder (@lukaseder) @ 2025-11-14 08:08 UTC (permalink / raw)
To: pgjdbc/pgjdbc <[email protected]>
> Well the except part is kinda important. It will probably break something
Sure, you can remove the trailing zeros like this:
```java
while (sb.charAt(sb.length() - 1) == '0')
sb.deleteCharAt(sb.length() - 1);
```
So:
```java
public String getValue() {
if (isNull) {
return null;
}
StringBuilder sb = new StringBuilder();
sb.append(years).append(" years ")
.append(months).append(" mons ")
.append(days).append(" days ")
.append(hours).append(" hours ")
.append(minutes).append(" mins ");
if (wholeSeconds < 0 || microSeconds < 0)
sb.append('-');
sb.append(Math.abs(wholeSeconds));
if (microSeconds != 0) {
sb.append('.');
String s = "" + Math.abs(microSeconds);
for (int i = s.length(); i < 6; i++)
sb.append('0');
sb.append(s);
while (sb.charAt(sb.length() - 1) == '0')
sb.deleteCharAt(sb.length() - 1);
}
sb.append(" secs");
return sb.toString();
}
```
^ permalink raw reply [nested|flat] 4+ messages in thread
end of thread, other threads:[~2025-11-14 08:08 UTC | newest]
Thread overview: 4+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-11-13 16:40 [pgjdbc/pgjdbc] issue #3865: Avoid well known slow JDK libraries String.format() and DecimalFormat in PGInterval::getValue to get drastic performance boost "lukaseder (@lukaseder)" <[email protected]>
2025-11-13 16:56 ` "davecramer (@davecramer)" <[email protected]>
2025-11-13 17:04 ` "vlsi (@vlsi)" <[email protected]>
2025-11-14 08:08 ` "lukaseder (@lukaseder)" <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox