Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: improve performance of resultSet.getObject #411

Merged
merged 3 commits into from Nov 3, 2015

Conversation

@vlsi
Copy link
Member

@vlsi vlsi commented Oct 29, 2015

There are two changes:

  1. Fast-path for long -> BigDecimal. For small values, database can send binary long, so there is no need to convert the received long back to string (see type=BIGINT below)
  2. "Cached getSQLType" speeds up getObject (see type=bigdecimal, getter=object below).

Before:

Benchmark                 (getter)  (ncols)  (nrows)     (type)  Mode  Cnt      Score    Error  Units
ProcessResultSet.execFetch    BEST        5     1000         INT  avgt    5   599,098 ± 11,030  us/op
ProcessResultSet.execFetch    BEST        5     1000  BIGDECIMAL  avgt    5  2392,760 ± 26,083  us/op

ProcessResultSet.execFetch    BEST        5     1000      BIGINT  avgt    5  3141,686 ± 92,846  us/op
ProcessResultSet.execFetch  OBJECT        5     1000  BIGDECIMAL  avgt    5  3400,252 ± 61,252  us/op

After:

Benchmark                 (getter)  (ncols)  (nrows)      (type)  Mode  Cnt     Score    Error  Units
ProcessResultSet.execFetch    BEST        5     1000         INT  avgt    5   598,402 ± 10,890  us/op
ProcessResultSet.execFetch    BEST        5     1000  BIGDECIMAL  avgt    5  2370,942 ± 31,716  us/op

ProcessResultSet.execFetch    BEST        5     1000      BIGINT  avgt    5   719,552 ±  9,449  us/op
ProcessResultSet.execFetch  OBJECT        5     1000  BIGDECIMAL  avgt    5  2450,741 ± 52,455  us/op
vlsi added 2 commits Oct 29, 2015
This change improves performance by eliminating TypeCache lookup on a per-row basis.
The first lookup is cached inside Field
@@ -2446,6 +2447,16 @@ public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException
checkResultSet(columnIndex);
if (wasNullFlag)
return null;

int sqlType = getSQLType(columnIndex);
Copy link
Contributor

@schlosna schlosna Oct 30, 2015

nit: is it worth deferring the getSQLType if the column is binary?

        if (isBinary(columnIndex)) {
            int sqlType = getSQLType(columnIndex);
            if (sqlType != Types.NUMERIC && sqlType != Types.DECIMAL) {
                Object obj = internalGetObject(columnIndex, fields[columnIndex - 1]);
                if (obj == null) return null;
                if (obj instanceof Long || obj instanceof Integer || obj instanceof Byte) {
                    return BigDecimal.valueOf(((Number) obj).longValue(), scale);
                }
                return toBigDecimal(trimMoney(String.valueOf(obj)), scale);
            }
        }

Copy link
Member Author

@vlsi vlsi Oct 30, 2015

I've moved that just in case. Thanks for the review.

@vlsi vlsi force-pushed the get_object_perf branch from 3e5bf5a to 33904ef Oct 30, 2015
@schlosna
Copy link
Contributor

@schlosna schlosna commented Oct 30, 2015

+1 LGTM

davecramer added a commit that referenced this issue Nov 3, 2015
perf: improve performance of resultSet.getObject
@davecramer davecramer merged commit 471f00a into pgjdbc:master Nov 3, 2015
1 check passed
@davecramer
Copy link
Member

@davecramer davecramer commented Nov 24, 2015

Yes, this has already been fixed in
#426

Thanks for the report

Dave Cramer

On 24 November 2015 at 02:59, Mario Ivankovits notifications@github.com
wrote:

Copied over my comment from the forked repository commit.
There is an interesting, but huge problem with this commit.

executing the following simple code snipped reveals that after 5
iterations the database switch to binary transfer and then the decoding at
line 2547 is using a scale of "-1" (the getBigDecimal default) instead of
"0".
PostgresSQL Version 9.4.4

Connection conn = DriverManager.getConnection(.....);
for (int i = 0; i<10; i++)
{
    PreparedStatement stmt = conn.prepareStatement("SELECT count(*) FROM pg_tables");
    ResultSet rs = stmt.executeQuery();
    rs.next();
    Number nb = rs.getBigDecimal(1);
    System.err.println("#" + i + "=" + nb);
    stmt.close();
}

Result:

#0=250
#1 #1
#2 #2
#3 #3
#4 #4
#5 #5
#6 #6
#7 #7
#8 #8
#9 #9

As a side note: It might be worth to check if internalGetObject already
returned a BigDecimal and so you can return that instead of converting it
to a string and then back to a bigdecimal using the trimMoney stuff.


Reply to this email directly or view it on GitHub
#411 (comment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

4 participants