Skip to content

Commit 48b79a3

Browse files
committed
feat: implement micro-benchmark module for performance testing
For instance, Statement create/close loop leads to OutOfMemory in OpenJDK/OracleJDK (finalizer queue fills up) -Xmx128m, OracleJDK 1.8u40, MacOS, 2.6Ghz Core i7 # Warmup Iteration 1: 1147,070 ns/op # Warmup Iteration 2: 12101,537 ns/op # Warmup Iteration 3: 90825,971 ns/op # Warmup Iteration 4: <failure> java.lang.OutOfMemoryError: GC overhead limit exceeded closes pgjdbc#289
1 parent c9214d3 commit 48b79a3

File tree

5 files changed

+404
-0
lines changed

5 files changed

+404
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ nbproject
99
*.iml
1010
build.local.properties
1111
*-dist.zip
12+
13+
ubenchmark/target

ubenchmark/pom.xml

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<!--
2+
Copyright (c) 1997-2011, PostgreSQL Global Development Group
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice,
9+
this list of conditions and the following disclaimer.
10+
2. Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
3. Neither the name of the PostgreSQL Global Development Group nor the names
14+
of its contributors may be used to endorse or promote products derived
15+
from this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
POSSIBILITY OF SUCH DAMAGE.
28+
-->
29+
30+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
31+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
32+
<modelVersion>4.0.0</modelVersion>
33+
34+
<groupId>org.postgresql</groupId>
35+
<artifactId>pgjdbc-benchmark</artifactId>
36+
<version>1.0</version>
37+
<packaging>jar</packaging>
38+
39+
<name>JDBC driver performance benchmarks</name>
40+
41+
<prerequisites>
42+
<maven>3.0</maven>
43+
</prerequisites>
44+
45+
<dependencies>
46+
<dependency>
47+
<groupId>org.openjdk.jmh</groupId>
48+
<artifactId>jmh-core</artifactId>
49+
<version>${jmh.version}</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.openjdk.jmh</groupId>
53+
<artifactId>jmh-generator-annprocess</artifactId>
54+
<version>${jmh.version}</version>
55+
<scope>provided</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.postgresql</groupId>
59+
<artifactId>postgresql</artifactId>
60+
<version>9.4-1201-jdbc41</version>
61+
<scope>provided</scope>
62+
</dependency>
63+
</dependencies>
64+
65+
<properties>
66+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
67+
<jmh.version>1.9.3</jmh.version>
68+
<javac.target>1.6</javac.target>
69+
<uberjar.name>benchmarks</uberjar.name>
70+
</properties>
71+
72+
<build>
73+
<plugins>
74+
<plugin>
75+
<groupId>org.apache.maven.plugins</groupId>
76+
<artifactId>maven-compiler-plugin</artifactId>
77+
<version>3.1</version>
78+
<configuration>
79+
<compilerVersion>${javac.target}</compilerVersion>
80+
<source>${javac.target}</source>
81+
<target>${javac.target}</target>
82+
</configuration>
83+
</plugin>
84+
<plugin>
85+
<groupId>org.apache.maven.plugins</groupId>
86+
<artifactId>maven-shade-plugin</artifactId>
87+
<version>2.2</version>
88+
<executions>
89+
<execution>
90+
<phase>package</phase>
91+
<goals>
92+
<goal>shade</goal>
93+
</goals>
94+
<configuration>
95+
<finalName>${uberjar.name}</finalName>
96+
<transformers>
97+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
98+
<mainClass>org.openjdk.jmh.Main</mainClass>
99+
</transformer>
100+
</transformers>
101+
<filters>
102+
<filter>
103+
<!--
104+
Shading signed JARs will fail without this.
105+
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
106+
-->
107+
<artifact>*:*</artifact>
108+
<excludes>
109+
<exclude>META-INF/*.SF</exclude>
110+
<exclude>META-INF/*.DSA</exclude>
111+
<exclude>META-INF/*.RSA</exclude>
112+
</excludes>
113+
</filter>
114+
</filters>
115+
</configuration>
116+
</execution>
117+
</executions>
118+
</plugin>
119+
</plugins>
120+
<pluginManagement>
121+
<plugins>
122+
<plugin>
123+
<artifactId>maven-clean-plugin</artifactId>
124+
<version>2.5</version>
125+
</plugin>
126+
<plugin>
127+
<artifactId>maven-deploy-plugin</artifactId>
128+
<version>2.8.1</version>
129+
</plugin>
130+
<plugin>
131+
<artifactId>maven-install-plugin</artifactId>
132+
<version>2.5.1</version>
133+
</plugin>
134+
<plugin>
135+
<artifactId>maven-jar-plugin</artifactId>
136+
<version>2.4</version>
137+
</plugin>
138+
<plugin>
139+
<artifactId>maven-javadoc-plugin</artifactId>
140+
<version>2.9.1</version>
141+
</plugin>
142+
<plugin>
143+
<artifactId>maven-resources-plugin</artifactId>
144+
<version>2.6</version>
145+
</plugin>
146+
<plugin>
147+
<artifactId>maven-site-plugin</artifactId>
148+
<version>3.3</version>
149+
</plugin>
150+
<plugin>
151+
<artifactId>maven-source-plugin</artifactId>
152+
<version>2.2.1</version>
153+
</plugin>
154+
<plugin>
155+
<artifactId>maven-surefire-plugin</artifactId>
156+
<version>2.17</version>
157+
</plugin>
158+
</plugins>
159+
</pluginManagement>
160+
</build>
161+
162+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.postgresql.benchmark.connection;
2+
3+
import org.openjdk.jmh.annotations.*;
4+
import org.openjdk.jmh.profile.GCProfiler;
5+
import org.openjdk.jmh.runner.Runner;
6+
import org.openjdk.jmh.runner.RunnerException;
7+
import org.openjdk.jmh.runner.options.Options;
8+
import org.openjdk.jmh.runner.options.OptionsBuilder;
9+
import org.postgresql.util.ConnectionUtil;
10+
11+
import java.sql.*;
12+
import java.util.Properties;
13+
import java.util.concurrent.TimeUnit;
14+
15+
/**
16+
* Tests the time and memory required to create a connection.
17+
* Note: due to TCP socket's turning into TIME_WAIT state on close, it is
18+
* rather hard to test lots of connection creations, so only 50 iterations are performed.
19+
*
20+
* <p>To run this and other benchmarks (you can run the class from within IDE):
21+
*
22+
* <blockquote>
23+
* <code>mvn package &amp;&amp;
24+
* java -classpath postgresql-driver.jar:target/benchmarks.jar -Duser=postgres -Dpassword=postgres -Dport=5433 -wi 10 -i 10 -f 1</code>
25+
* </blockquote>
26+
*
27+
* <p>To run with profiling:
28+
*
29+
* <blockquote>
30+
* <code>java -classpath postgresql-driver.jar:target/benchmarks.jar
31+
* -prof gc -f 1 -wi 10 -i 10</code>
32+
* </blockquote>
33+
*/
34+
@Fork(1)
35+
@Measurement(iterations = 50)
36+
@Warmup(iterations = 10)
37+
@State(Scope.Thread)
38+
@Threads(1)
39+
@BenchmarkMode(Mode.SingleShotTime)
40+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
41+
public class FinalizeConnection
42+
{
43+
private Properties connectionProperties;
44+
private String connectionUrl;
45+
private Driver driver;
46+
47+
@Setup(Level.Trial)
48+
public void setUp() throws SQLException
49+
{
50+
Properties props = ConnectionUtil.getProperties();
51+
52+
connectionProperties = props;
53+
connectionUrl = ConnectionUtil.getURL();
54+
driver = DriverManager.getDriver(connectionUrl);
55+
}
56+
57+
@Benchmark
58+
public void baseline() throws SQLException
59+
{
60+
}
61+
62+
@Benchmark
63+
public Connection createAndClose() throws SQLException
64+
{
65+
Connection connection = driver.connect(connectionUrl, connectionProperties);
66+
connection.close();
67+
return connection;
68+
}
69+
70+
public static void main(String[] args) throws RunnerException
71+
{
72+
Options opt = new OptionsBuilder()
73+
.include(FinalizeConnection.class.getSimpleName())
74+
.addProfiler(GCProfiler.class)
75+
.detectJvmArgs()
76+
.build();
77+
78+
new Runner(opt).run();
79+
}
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package org.postgresql.benchmark.statement;
2+
3+
import org.openjdk.jmh.annotations.*;
4+
import org.openjdk.jmh.profile.GCProfiler;
5+
import org.openjdk.jmh.runner.Runner;
6+
import org.openjdk.jmh.runner.RunnerException;
7+
import org.openjdk.jmh.runner.options.Options;
8+
import org.openjdk.jmh.runner.options.OptionsBuilder;
9+
import org.postgresql.util.ConnectionUtil;
10+
11+
import java.sql.Connection;
12+
import java.sql.DriverManager;
13+
import java.sql.SQLException;
14+
import java.sql.Statement;
15+
import java.util.Properties;
16+
import java.util.concurrent.ThreadLocalRandom;
17+
import java.util.concurrent.TimeUnit;
18+
19+
/**
20+
* Here we measure the time it takes to create and close a dummy statement.
21+
*
22+
* <p>To run this and other benchmarks (you can run the class from within IDE):
23+
*
24+
* <blockquote>
25+
* <code>mvn package &amp;&amp;
26+
* java -classpath postgresql-driver.jar:target/benchmarks.jar -Duser=postgres -Dpassword=postgres -Dport=5433 -wi 10 -i 10 -f 1</code>
27+
* </blockquote>
28+
*
29+
* <p>To run with profiling:
30+
*
31+
* <blockquote>
32+
* <code>java -classpath postgresql-driver.jar:target/benchmarks.jar
33+
* -prof gc -f 1 -wi 10 -i 10</code>
34+
* </blockquote>
35+
*/
36+
@Fork(value = 1, jvmArgsPrepend = "-Xmx128m")
37+
@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
38+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
39+
@State(Scope.Thread)
40+
@BenchmarkMode(Mode.AverageTime)
41+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
42+
public class FinalizeStatement
43+
{
44+
@Param({"0", "1", "10", "100"})
45+
private int leakPct;
46+
47+
private float leakPctFloat;
48+
49+
private Connection connection;
50+
51+
@Setup(Level.Trial)
52+
public void setUp() throws SQLException
53+
{
54+
Properties props = ConnectionUtil.getProperties();
55+
56+
connection = DriverManager.getConnection(ConnectionUtil.getURL(), props);
57+
leakPctFloat = 0.01f * leakPct;
58+
}
59+
60+
@TearDown(Level.Trial)
61+
public void tearDown() throws SQLException
62+
{
63+
connection.close();
64+
}
65+
66+
@Benchmark
67+
public Statement createAndLeak() throws SQLException
68+
{
69+
Statement statement = connection.createStatement();
70+
if (ThreadLocalRandom.current().nextFloat() >= leakPctFloat)
71+
statement.close();
72+
return statement;
73+
}
74+
75+
public static void main(String[] args) throws RunnerException
76+
{
77+
Options opt = new OptionsBuilder()
78+
.include(FinalizeStatement.class.getSimpleName())
79+
.addProfiler(GCProfiler.class)
80+
.detectJvmArgs()
81+
.build();
82+
83+
new Runner(opt).run();
84+
}
85+
}

0 commit comments

Comments
 (0)