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