1515
1616package software .amazon .awssdk .benchmark ;
1717
18- import com .fasterxml .jackson .core .JsonProcessingException ;
18+ import static software .amazon .awssdk .benchmark .utils .BenchmarkConstant .OBJECT_MAPPER ;
19+
20+ import java .io .IOException ;
21+ import java .io .OutputStream ;
22+ import java .nio .file .Files ;
23+ import java .nio .file .Path ;
24+ import java .nio .file .Paths ;
1925import java .util .ArrayList ;
2026import java .util .Arrays ;
2127import java .util .Collection ;
2228import java .util .List ;
29+ import java .util .stream .Collectors ;
30+ import org .apache .commons .cli .CommandLine ;
31+ import org .apache .commons .cli .CommandLineParser ;
32+ import org .apache .commons .cli .DefaultParser ;
33+ import org .apache .commons .cli .Options ;
34+ import org .apache .commons .cli .ParseException ;
2335import org .openjdk .jmh .results .RunResult ;
2436import org .openjdk .jmh .runner .Runner ;
2537import org .openjdk .jmh .runner .RunnerException ;
4557import software .amazon .awssdk .benchmark .enhanced .dynamodb .EnhancedClientQueryV1MapperComparisonBenchmark ;
4658import software .amazon .awssdk .benchmark .enhanced .dynamodb .EnhancedClientScanV1MapperComparisonBenchmark ;
4759import software .amazon .awssdk .benchmark .enhanced .dynamodb .EnhancedClientUpdateV1MapperComparisonBenchmark ;
60+ import software .amazon .awssdk .benchmark .stats .SdkBenchmarkResult ;
61+ import software .amazon .awssdk .benchmark .utils .BenchmarkProcessorOutput ;
4862import software .amazon .awssdk .utils .Logger ;
4963
5064
@@ -84,13 +98,15 @@ public class BenchmarkRunner {
8498
8599 private final List <String > benchmarksToRun ;
86100 private final BenchmarkResultProcessor resultProcessor ;
101+ private final BenchmarkRunnerOptions options ;
87102
88- private BenchmarkRunner (List <String > benchmarksToRun ) {
103+ private BenchmarkRunner (List <String > benchmarksToRun , BenchmarkRunnerOptions options ) {
89104 this .benchmarksToRun = benchmarksToRun ;
90105 this .resultProcessor = new BenchmarkResultProcessor ();
106+ this .options = options ;
91107 }
92108
93- public static void main (String ... args ) throws RunnerException , JsonProcessingException {
109+ public static void main (String ... args ) throws Exception {
94110 List <String > benchmarksToRun = new ArrayList <>();
95111 benchmarksToRun .addAll (SYNC_BENCHMARKS );
96112 benchmarksToRun .addAll (ASYNC_BENCHMARKS );
@@ -99,13 +115,14 @@ public static void main(String... args) throws RunnerException, JsonProcessingEx
99115
100116 log .info (() -> "Skipping tests, to reduce benchmark times: \n " + MAPPER_BENCHMARKS + "\n " + METRIC_BENCHMARKS );
101117
102-
103- BenchmarkRunner runner = new BenchmarkRunner (benchmarksToRun );
118+ BenchmarkRunner runner = new BenchmarkRunner (benchmarksToRun , parseOptions (args ));
104119
105120 runner .runBenchmark ();
106121 }
107122
108123 private void runBenchmark () throws RunnerException {
124+ log .info (() -> "Running with options: " + options );
125+
109126 ChainedOptionsBuilder optionsBuilder = new OptionsBuilder ();
110127
111128 benchmarksToRun .forEach (optionsBuilder ::include );
@@ -114,11 +131,70 @@ private void runBenchmark() throws RunnerException {
114131
115132 Collection <RunResult > results = new Runner (optionsBuilder .build ()).run ();
116133
117- List <String > failedResult = resultProcessor .processBenchmarkResult (results );
134+ BenchmarkProcessorOutput processedResults = resultProcessor .processBenchmarkResult (results );
135+ List <String > failedResults = processedResults .getFailedBenchmarks ();
136+
137+ if (options .outputPath != null ) {
138+ log .info (() -> "Writing results to " + options .outputPath );
139+ writeResults (processedResults , options .outputPath );
140+ }
141+
142+ if (options .check && !failedResults .isEmpty ()) {
143+ log .info (() -> "Failed perf regression tests: " + failedResults );
144+ throw new RuntimeException ("Perf regression tests failed: " + failedResults );
145+ }
146+ }
147+
148+ private static BenchmarkRunnerOptions parseOptions (String [] args ) throws ParseException {
149+ Options cliOptions = new Options ();
150+ cliOptions .addOption ("o" , "output" , true ,
151+ "The path to write the benchmark results to." );
152+ cliOptions .addOption ("c" , "check" , false ,
153+ "If specified, exit with error code 1 if the results are not within the baseline." );
154+
155+ CommandLineParser parser = new DefaultParser ();
156+ CommandLine cmdLine = parser .parse (cliOptions , args );
157+
158+ BenchmarkRunnerOptions options = new BenchmarkRunnerOptions ()
159+ .check (cmdLine .hasOption ("c" ));
160+
161+ if (cmdLine .hasOption ("o" )) {
162+ options .outputPath (Paths .get (cmdLine .getOptionValue ("o" )));
163+ }
164+
165+ return options ;
166+ }
167+
168+ private static void writeResults (BenchmarkProcessorOutput output , Path outputPath ) {
169+ List <SdkBenchmarkResult > results = output .getBenchmarkResults ().values ().stream ().collect (Collectors .toList ());
170+ try (OutputStream os = Files .newOutputStream (outputPath )) {
171+ OBJECT_MAPPER .writeValue (os , results );
172+ } catch (IOException e ) {
173+ log .error (() -> "Failed to write the results to " + outputPath , e );
174+ throw new RuntimeException (e );
175+ }
176+ }
177+
178+ private static class BenchmarkRunnerOptions {
179+ private Path outputPath ;
180+ private boolean check ;
181+
182+ public BenchmarkRunnerOptions outputPath (Path outputPath ) {
183+ this .outputPath = outputPath ;
184+ return this ;
185+ }
186+
187+ public BenchmarkRunnerOptions check (boolean check ) {
188+ this .check = check ;
189+ return this ;
190+ }
118191
119- if (!failedResult .isEmpty ()) {
120- log .info (() -> "Failed perf regression tests: " + failedResult );
121- throw new RuntimeException ("Perf regression tests failed: " + failedResult );
192+ @ Override
193+ public String toString () {
194+ return "BenchmarkRunnerOptions{" +
195+ "outputPath=" + outputPath +
196+ ", check=" + check +
197+ '}' ;
122198 }
123199 }
124200}
0 commit comments