1414
1515package com .github .pgasync ;
1616
17- import com .pgasync .Connection ;
1817import com .pgasync .ConnectionPool ;
18+ import com .pgasync .PreparedStatement ;
1919import org .junit .*;
2020import org .junit .runner .RunWith ;
2121import org .junit .runners .Parameterized ;
2222import org .junit .runners .Parameterized .Parameters ;
2323
2424import java .util .*;
2525import java .util .concurrent .*;
26+ import java .util .concurrent .atomic .LongAdder ;
2627import java .util .function .Function ;
2728import java .util .stream .IntStream ;
29+ import java .util .stream .LongStream ;
2830
2931import static com .github .pgasync .DatabaseRule .createPoolBuilder ;
3032import static java .lang .System .currentTimeMillis ;
3133import static java .lang .System .out ;
32- import static java .util .concurrent .TimeUnit .MILLISECONDS ;
3334import static org .junit .runners .MethodSorters .NAME_ASCENDING ;
3435
3536@ RunWith (Parameterized .class )
3637@ FixMethodOrder (NAME_ASCENDING )
3738public class PerformanceTest {
3839
40+ private static final String SELECT_42 = "select 42" ;
41+
3942 @ ClassRule
4043 public static DatabaseRule dbr = new DatabaseRule (createPoolBuilder (1 ));
4144
@@ -69,7 +72,8 @@ public PerformanceTest(int poolSize, int numThreads) {
6972 this .numThreads = numThreads ;
7073 pool = dbr .builder
7174 .password ("async-pg" )
72- .maxConnections (poolSize ).build ();
75+ .maxConnections (poolSize )
76+ .build ();
7377 }
7478
7579 @ After
@@ -79,140 +83,96 @@ public void close() {
7983
8084 @ Test (timeout = 2000 )
8185 public void t1_preAllocatePool () throws Exception {
82- List <Connection > connections = new ArrayList <>();
8386 CompletableFuture .allOf ((CompletableFuture <?>[]) IntStream .range (0 , poolSize )
84- .mapToObj (i -> pool .getConnection ().thenAccept (connections ::add ))
87+ .mapToObj (i -> pool .getConnection ()
88+ .thenApply (connection ->
89+ connection .prepareStatement (SELECT_42 )
90+ .thenApply (PreparedStatement ::close )
91+ .thenCompose (Function .identity ())
92+ .thenApply (v -> connection .close ())
93+ .thenCompose (Function .identity ())
94+ )
95+ .thenCompose (Function .identity ())
96+ )
8597 .toArray (size -> new CompletableFuture <?>[size ])
8698 ).get ();
87- connections .forEach (Connection ::close );
8899 }
89100
90101 @ Test
91- public void t3_run () throws Exception {
92- Collection <Callable <Long >> tasks = new ArrayList <>();
93- for (int i = 0 ; i < batchSize ; ++i ) {
94- tasks .add (new Callable <>() {
95- final Exchanger <Long > swap = new Exchanger <>();
96-
97- @ Override
98- public Long call () throws Exception {
99-
100- pool .getConnection ()
101- .thenApply (connection -> connection .prepareStatement ("select 42" )
102- .thenApply (stmt ->
103- stmt .query ()
104- .thenAccept (res -> {
105- try {
106- swap .exchange (currentTimeMillis ());
107- } catch (Exception e ) {
108- throw new AssertionError (e );
109- }
110- })
111- .handle ((v , th ) ->
112- stmt .close ()
113- .thenAccept (_v -> {
114- if (th != null )
115- throw new RuntimeException (th );
116- })
117- )
118- .thenCompose (Function .identity ())
119- )
120- .thenCompose (Function .identity ())
121- .handle ((v , th ) -> connection .close ()
122- .thenAccept (_v -> {
123- if (th != null ) {
124- throw new RuntimeException (th );
125- }
126- }))
127- .thenCompose (Function .identity ())
128- )
129- .thenCompose (Function .identity ())
130- .exceptionally (th -> {
131- throw new AssertionError (th );
132- });
133-
134- /*
135- pool.completeScript("select 42")
136- .thenAccept(r -> {
137- try {
138- swap.exchange(currentTimeMillis());
139- } catch (Exception e) {
140- throw new AssertionError(e);
141- }
142- })
143- .exceptionally(th -> {
144- throw new AssertionError(th);
145- });
146- */
147- return swap .exchange (null );
148- }
149- });
150- }
151-
152- long minTime = Long .MAX_VALUE ;
153-
154- for (int r = 0 ; r < repeats ; ++r ) {
155- MILLISECONDS .sleep (300 );
156-
157- final CyclicBarrier barrier = new CyclicBarrier (numThreads + 1 );
158-
159- final Queue <Callable <Long >> taskQueue = new LinkedBlockingQueue <>(tasks );
160- final Queue <Long > endTimes = new ArrayBlockingQueue <>(batchSize );
161-
162- Thread [] threads = new Thread [numThreads ];
163- for (int i = 0 ; i < numThreads ; ++i ) {
164- threads [i ] = new Thread ("tester" + i ) {
165- public void run () {
166- try {
167- barrier .await ();
168- } catch (InterruptedException | BrokenBarrierException e ) {
169- e .printStackTrace ();
170- }
171-
172- Callable <Long > c ;
173- try {
174- while ((c = taskQueue .poll ()) != null ) {
175- endTimes .add (c .call ());
176- }
177- } catch (Exception e ) {
178- e .printStackTrace ();
179- }
102+ public void t3_run () {
103+ double mean = LongStream .range (0 , repeats )
104+ .map (i -> {
105+ try {
106+ return performBatch ();
107+ } catch (Exception ex ) {
108+ throw new RuntimeException (ex );
180109 }
181- };
182- threads [i ].start ();
183- }
184-
185- long start = currentTimeMillis ();
186- barrier .await ();
110+ })
111+ .average ().getAsDouble ();
112+ results .computeIfAbsent (poolSize + " conn" , k -> new TreeMap <>())
113+ .put (numThreads , Math .round (mean ));
114+ }
187115
188- for (Thread thread : threads ) {
189- thread .join ();
190- }
116+ private long performBatch () throws Exception {
117+ List <CompletableFuture <Void >> batchFutures = new ArrayList <>();
118+ long startTime = currentTimeMillis ();
119+ for (int i = 0 ; i < batchSize ; i ++) {
120+
121+ batchFutures .add (pool .getConnection ()
122+ .thenApply (connection -> connection .prepareStatement (SELECT_42 )
123+ .thenApply (stmt -> {
124+ return stmt .query ()
125+ .handle ((v , th ) ->
126+ stmt .close ()
127+ .thenAccept (_v -> {
128+ if (th != null ) {
129+ throw new RuntimeException (th );
130+ }
131+ })
132+ )
133+ .thenCompose (Function .identity ());
134+ }
135+ )
136+ .thenCompose (Function .identity ())
137+ .handle ((v , th ) -> connection .close ()
138+ .thenAccept (_v -> {
139+ if (th != null ) {
140+ throw new RuntimeException (th );
141+ }
142+ }))
143+ .thenCompose (Function .identity ())
144+ )
145+ .thenCompose (Function .identity ())
146+ .exceptionally (th -> {
147+ throw new AssertionError (th );
148+ }));
191149
192- OptionalLong end = endTimes .stream ().mapToLong (f -> f ).max ();
193- long time = end .getAsLong () - start ;
194- minTime = Math .min (minTime , time );
150+ /*
151+ batchFutures.add(pool.completeScript("select 42").thenAccept(rs -> {
152+ }));
153+ */
195154 }
196-
197- results .get (key (poolSize )).put (numThreads , minTime );
198-
199- out .printf ("\t %d\t %2d\t %4.3f\t %n" , poolSize , numThreads , minTime / 1000.0 );
155+ CompletableFuture
156+ .allOf (batchFutures .toArray (new CompletableFuture <?>[]{}))
157+ .get ();
158+ long duration = currentTimeMillis () - startTime ;
159+ return duration ;
200160 }
201161
202162 @ AfterClass
203163 public static void printResults () {
204164 out .println ();
205165 out .println ("Requests per second, Hz:" );
206- out .print (" threads" );
207- results .keySet ().forEach (i -> out .printf ("\t \t %s\t " , i ));
166+ out .print (" threads" );
167+ results .keySet ().forEach (i -> out .printf ("\t %s\t " , i ));
208168 out .println ();
209169
210170 results .values ().iterator ().next ().keySet ().forEach (threads -> {
211171 out .print (" " + threads );
212- results .keySet ().forEach (conns -> {
213- long millis = results .get (conns ).get (threads );
214- double rps = batchSize * 1000 / (double ) millis ;
215- out .printf ("\t \t %f " , rps );
172+ results .keySet ().forEach (connections -> {
173+ long batchDuration = results .get (connections ).get (threads );
174+ double rps = 1000 * batchSize / (double ) batchDuration ;
175+ out .printf ("\t \t %d " , Math . round ( rps ) );
216176 });
217177 out .println ();
218178 });
0 commit comments