2525package jdk .graal .compiler .hotspot .replaycomp ;
2626
2727import static jdk .graal .compiler .core .common .NativeImageSupport .inRuntimeCode ;
28+ import static jdk .graal .compiler .serviceprovider .GraalServices .getCurrentThreadAllocatedBytes ;
29+ import static jdk .graal .compiler .serviceprovider .GraalServices .getCurrentThreadCpuTime ;
2830
2931import java .io .Closeable ;
3032import java .io .FileReader ;
4345
4446import org .graalvm .collections .EconomicMap ;
4547
48+ import jdk .graal .compiler .code .CompilationResult ;
4649import jdk .graal .compiler .core .GraalCompilerOptions ;
4750import jdk .graal .compiler .core .common .LibGraalSupport ;
4851import jdk .graal .compiler .debug .GlobalMetrics ;
52+ import jdk .graal .compiler .debug .PathUtilities ;
4953import jdk .graal .compiler .hotspot .CompilerConfigurationFactory ;
5054import jdk .graal .compiler .hotspot .HotSpotGraalCompiler ;
5155import jdk .graal .compiler .hotspot .HotSpotGraalCompilerFactory ;
5660import jdk .graal .compiler .hotspot .Platform ;
5761import jdk .graal .compiler .options .OptionValues ;
5862import jdk .graal .compiler .util .args .BooleanValue ;
63+ import jdk .graal .compiler .util .args .Command ;
64+ import jdk .graal .compiler .util .args .CommandGroup ;
5965import jdk .graal .compiler .util .args .IntegerValue ;
6066import jdk .graal .compiler .util .args .OptionValue ;
6167import jdk .graal .compiler .util .args .Program ;
@@ -118,33 +124,111 @@ public int getStatus() {
118124 @ SuppressWarnings ("try" )
119125 public static ExitStatus run (String [] args , PrintStream out ) {
120126 Program program = new Program ("mx replaycomp" , "Replay compilations from files." );
121- OptionValue <Boolean > benchmarkArg = program .addNamed ("--benchmark" , new BooleanValue ("true|false" , false , "Replay compilations as a benchmark." ));
122- OptionValue <Integer > iterationsArg = program .addNamed ("--iterations" , new IntegerValue ("n" , 10 , "The number of benchmark iterations." ));
123127 OptionValue <Boolean > compareGraphsArg = program .addNamed ("--compare-graphs" , new BooleanValue ("true|false" , false , "Verify that the replayed graph equals the recorded one." ));
124128 OptionValue <String > inputPathArg = program .addPositional (new StringValue ("TARGET" , "Path to a directory with replay compilation files (or path to a single file)." ));
129+ CommandGroup <LauncherCommand > commandGroup = new CommandGroup <>("COMMAND" , new ReplayCommand (), "The mode to replay in." );
130+ commandGroup .addCommand (new BenchmarkCommand ());
131+ program .addCommandGroup (commandGroup );
125132 program .parseAndValidate (args , true );
126- Path inputPath = Path .of (inputPathArg .getValue ());
127- List <Path > inputFiles ;
128- try {
129- inputFiles = findJsonFiles (inputPath );
130- } catch (IOException e ) {
131- out .println (e .getMessage ());
133+ List <Path > inputFiles = getInputFiles (out , inputPathArg );
134+ if (inputFiles == null ) {
132135 return ExitStatus .Failure ;
133136 }
134- if (inputFiles .isEmpty ()) {
135- out .println ("No replay files found in " + inputPath );
136- return ExitStatus .Failure ;
137+ return commandGroup .getSelectedCommand ().run (out , inputFiles , compareGraphsArg .getValue ());
138+ }
139+
140+ /**
141+ * A command implementing one use case of the replay compilation launcher.
142+ */
143+ private abstract static class LauncherCommand extends Command {
144+ /**
145+ * Constructs a launcher command.
146+ *
147+ * @param name the name of the command
148+ * @param description the description of the command
149+ */
150+ private LauncherCommand (String name , String description ) {
151+ super (name , description );
137152 }
138153
139- OptionValues systemOptions = new OptionValues (HotSpotGraalOptionValues .parseOptions ());
140- OptionValues options = new OptionValues (systemOptions , GraalCompilerOptions .SystemicCompilationFailureRate , 0 );
141- CompilerInterfaceDeclarations declarations = CompilerInterfaceDeclarations .build ();
142- HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime .runtime ();
143- CompilerConfigurationFactory factory = CompilerConfigurationFactory .selectFactory (null , options , runtime );
154+ /**
155+ * Runs the command.
156+ *
157+ * @param out the stream for output
158+ * @param inputFiles the files that should be replayed
159+ * @param compareGraphs whether the replayed graph should be compared with the recorded one
160+ * @return the exit status of the launcher
161+ */
162+ public abstract ExitStatus run (PrintStream out , List <Path > inputFiles , boolean compareGraphs );
163+ }
164+
165+ /**
166+ * A command that replays a set of files (e.g., for debugging purposes).
167+ */
168+ private static final class ReplayCommand extends LauncherCommand {
169+ private ReplayCommand () {
170+ super ("--replay" , "Replay compilations." );
171+ }
144172
145- LibGraalSupport libgraal = LibGraalSupport .INSTANCE ;
146- GlobalMetrics globalMetrics = new GlobalMetrics ();
147- if (benchmarkArg .getValue ()) {
173+ @ SuppressWarnings ("try" )
174+ @ Override
175+ public ExitStatus run (PrintStream out , List <Path > inputFiles , boolean compareGraphs ) {
176+ OptionValues systemOptions = new OptionValues (HotSpotGraalOptionValues .parseOptions ());
177+ OptionValues options = new OptionValues (systemOptions , GraalCompilerOptions .SystemicCompilationFailureRate , 0 );
178+ CompilerInterfaceDeclarations declarations = CompilerInterfaceDeclarations .build ();
179+ HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime .runtime ();
180+ CompilerConfigurationFactory factory = CompilerConfigurationFactory .selectFactory (null , options , runtime );
181+ LibGraalSupport libgraal = LibGraalSupport .INSTANCE ;
182+ GlobalMetrics globalMetrics = new GlobalMetrics ();
183+ ReplayTaskStatistics statistics = new ReplayTaskStatistics ();
184+ for (Path file : inputFiles ) {
185+ ReplayCompilationTask task = statistics .startTask (file .toString ());
186+ try (AutoCloseable ignored = libgraal != null ? libgraal .openCompilationRequestScope () : null ;
187+ Reproducer reproducer = Reproducer .initializeFromFile (file .toString (), declarations , runtime ,
188+ options , factory , globalMetrics , out , EconomicMap .create ())) {
189+ reproducer .compile ().verify (compareGraphs );
190+ out .println ("Successfully replayed " + reproducer .request );
191+ } catch (ReplayParserFailure failure ) {
192+ out .println ("Replay failed: " + failure .getMessage ());
193+ task .setFailureReason (failure .getMessage ());
194+ } catch (Exception e ) {
195+ out .println ("Replay failed: " + e );
196+ e .printStackTrace (out );
197+ return ExitStatus .Failure ;
198+ }
199+ }
200+ out .println ();
201+ statistics .printStatistics (out );
202+ globalMetrics .print (options );
203+ return ExitStatus .Success ;
204+ }
205+ }
206+
207+ private static final double ONE_MILLION = 1_000_000d ;
208+
209+ /**
210+ * A command that runs replay compilation as a benchmark.
211+ */
212+ private static final class BenchmarkCommand extends LauncherCommand {
213+ private final OptionValue <Integer > iterationsArg ;
214+
215+ private final OptionValue <String > resultsFileArg ;
216+
217+ private BenchmarkCommand () {
218+ super ("--benchmark" , "Replay compilations as a benchmark." );
219+ iterationsArg = addNamed ("--iterations" , new IntegerValue ("N" , 10 , "The number of benchmark iterations." ));
220+ resultsFileArg = addNamed ("--results-file" , new StringValue ("RESULTS_FILE" , null , "Write benchmark metrics to the file in CSV format." ));
221+ }
222+
223+ @ SuppressWarnings ("try" )
224+ @ Override
225+ public ExitStatus run (PrintStream out , List <Path > inputFiles , boolean compareGraphs ) {
226+ OptionValues options = new OptionValues (HotSpotGraalOptionValues .parseOptions ());
227+ CompilerInterfaceDeclarations declarations = CompilerInterfaceDeclarations .build ();
228+ HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime .runtime ();
229+ CompilerConfigurationFactory factory = CompilerConfigurationFactory .selectFactory (null , options , runtime );
230+ LibGraalSupport libgraal = LibGraalSupport .INSTANCE ;
231+ GlobalMetrics globalMetrics = new GlobalMetrics ();
148232 List <Reproducer > reproducers = new ArrayList <>();
149233 EconomicMap <Object , Object > internPool = EconomicMap .create ();
150234 for (Path file : inputFiles ) {
@@ -160,59 +244,65 @@ public static ExitStatus run(String[] args, PrintStream out) {
160244 }
161245 }
162246 internPool .clear ();
163- Runtime javaRuntime = Runtime .getRuntime ();
164- for (int i = 0 ; i < iterationsArg .getValue (); i ++) {
165- out .printf ("====== replaycomp iteration %d started ======%n" , i );
166- double memBefore = (javaRuntime .totalMemory () - javaRuntime .freeMemory ()) / 1_000_000d ;
167- long before = System .nanoTime ();
168- System .gc ();
169- long afterGC = System .nanoTime ();
170- double memAfter = (javaRuntime .totalMemory () - javaRuntime .freeMemory ()) / 1_000_000d ;
171- double gcMillis = (afterGC - before ) / 1_000_000d ;
172- out .printf ("GC before operation: completed in %.3f ms, heap usage %.3f MB -> %.3f MB.%n" , gcMillis , memBefore , memAfter );
173- int codeHash = 0 ;
174- for (Reproducer reproducer : reproducers ) {
175- try (AutoCloseable ignored = libgraal != null ? libgraal .openCompilationRequestScope () : null ) {
176- ReplayResult replayResult = reproducer .compile ();
177- replayResult .verify (compareGraphsArg .getValue ());
178- codeHash = codeHash * 31 + Arrays .hashCode (replayResult .replayedArtifacts ().result ().getTargetCode ());
179- } catch (Exception e ) {
180- out .println ("Replay failed: " + e );
181- e .printStackTrace (out );
182- return ExitStatus .Failure ;
247+ if (reproducers .isEmpty ()) {
248+ out .println ("There are no compilations to replay" );
249+ return ExitStatus .Failure ;
250+ }
251+ try (PrintStream outStat = (resultsFileArg .isSet ()) ? new PrintStream (PathUtilities .openOutputStream (resultsFileArg .getValue ())) : null ) {
252+ for (int i = 0 ; i < iterationsArg .getValue (); i ++) {
253+ performCollection (out );
254+ BenchmarkIterationMetrics metrics = new BenchmarkIterationMetrics (i );
255+ metrics .beginIteration (out , outStat );
256+ for (Reproducer reproducer : reproducers ) {
257+ try (AutoCloseable ignored = libgraal != null ? libgraal .openCompilationRequestScope () : null ) {
258+ ReplayResult replayResult = reproducer .compile ();
259+ replayResult .verify (compareGraphs );
260+ metrics .addVerifiedResult (replayResult );
261+ } catch (Exception e ) {
262+ out .println ("Replay failed: " + e );
263+ e .printStackTrace (out );
264+ return ExitStatus .Failure ;
265+ }
183266 }
267+ metrics .endIteration (out , outStat );
184268 }
185- out .printf ("Compiled code hash: %d%n" , codeHash );
186- long after = System .nanoTime ();
187- double iterMillis = (after - before ) / 1_000_000d ;
188- out .printf ("====== replaycomp iteration %d completed (%.3f ms) ======%n" , i , iterMillis );
269+ } catch (IOException e ) {
270+ out .println ("Failed to write benchmark statistics to " + resultsFileArg .getValue ());
271+ return ExitStatus .Failure ;
189272 }
190273 for (Reproducer reproducer : reproducers ) {
191274 reproducer .close ();
192275 }
193- } else {
194- ReplayCompilationStatistics statistics = new ReplayCompilationStatistics ();
195- for (Path file : inputFiles ) {
196- ReplayCompilationTask task = statistics .startTask (file .toString ());
197- try (AutoCloseable ignored = libgraal != null ? libgraal .openCompilationRequestScope () : null ;
198- Reproducer reproducer = Reproducer .initializeFromFile (file .toString (), declarations , runtime ,
199- options , factory , globalMetrics , out , EconomicMap .create ())) {
200- reproducer .compile ().verify (compareGraphsArg .getValue ());
201- out .println ("Successfully replayed " + reproducer .request );
202- } catch (ReplayParserFailure failure ) {
203- out .println ("Replay failed: " + failure .getMessage ());
204- task .setFailureReason (failure .getMessage ());
205- } catch (Exception e ) {
206- out .println ("Replay failed: " + e );
207- e .printStackTrace (out );
208- return ExitStatus .Failure ;
209- }
210- }
211- out .println ();
212- statistics .printStatistics (out );
276+ globalMetrics .print (options );
277+ return ExitStatus .Success ;
278+ }
279+
280+ private static void performCollection (PrintStream out ) {
281+ Runtime javaRuntime = Runtime .getRuntime ();
282+ double memBefore = (javaRuntime .totalMemory () - javaRuntime .freeMemory ()) / ONE_MILLION ;
283+ long gcBeforeTimestamp = System .nanoTime ();
284+ System .gc ();
285+ long gcAfterTimestamp = System .nanoTime ();
286+ double memAfter = (javaRuntime .totalMemory () - javaRuntime .freeMemory ()) / ONE_MILLION ;
287+ double gcMillis = (gcAfterTimestamp - gcBeforeTimestamp ) / ONE_MILLION ;
288+ out .printf ("GC before operation: completed in %.3f ms, heap usage %.3f MB -> %.3f MB.%n" , gcMillis , memBefore , memAfter );
213289 }
214- globalMetrics .print (options );
215- return ExitStatus .Success ;
290+ }
291+
292+ private static List <Path > getInputFiles (PrintStream out , OptionValue <String > inputPathArg ) {
293+ Path inputPath = Path .of (inputPathArg .getValue ());
294+ List <Path > inputFiles ;
295+ try {
296+ inputFiles = findJsonFiles (inputPath );
297+ } catch (IOException e ) {
298+ out .println (e .getMessage ());
299+ return null ;
300+ }
301+ if (inputFiles .isEmpty ()) {
302+ out .println ("No replay files found in " + inputPath );
303+ return null ;
304+ }
305+ return inputFiles ;
216306 }
217307
218308 /**
@@ -434,10 +524,10 @@ public boolean isFailure() {
434524 * Tracks the outcomes of all replayed compilations, which is used to print summary statistics
435525 * at the end.
436526 */
437- private static class ReplayCompilationStatistics {
527+ private static class ReplayTaskStatistics {
438528 private final List <ReplayCompilationTask > tasks ;
439529
440- ReplayCompilationStatistics () {
530+ ReplayTaskStatistics () {
441531 this .tasks = new ArrayList <>();
442532 }
443533
@@ -468,4 +558,62 @@ private List<ReplayCompilationTask> failedTasks() {
468558 return tasks .stream ().filter (ReplayCompilationTask ::isFailure ).toList ();
469559 }
470560 }
561+
562+ /**
563+ * Collects and prints compilation metrics for one iteration of a replay benchmark.
564+ */
565+ private static final class BenchmarkIterationMetrics {
566+ private final int iteration ;
567+
568+ private long beginWallTime ;
569+
570+ private long beginThreadTime ;
571+
572+ private long beginMemory ;
573+
574+ private int compiledBytecodes ;
575+
576+ private int targetCodeSize ;
577+
578+ private int targetCodeHash ;
579+
580+ private BenchmarkIterationMetrics (int iteration ) {
581+ this .iteration = iteration ;
582+ }
583+
584+ public void beginIteration (PrintStream out , PrintStream outStat ) {
585+ if (iteration == 0 && outStat != null ) {
586+ outStat .println ("iteration,wall_time_ns,thread_time_ns,allocated_memory,compiled_bytecodes,target_code_size,target_code_hash" );
587+ }
588+ out .printf ("====== replaycomp iteration %d started ======%n" , iteration );
589+ beginMemory = getCurrentThreadAllocatedBytes ();
590+ beginWallTime = System .nanoTime ();
591+ beginThreadTime = getCurrentThreadCpuTime ();
592+ }
593+
594+ public void addVerifiedResult (ReplayResult replayResult ) {
595+ CompilationResult result = replayResult .replayedArtifacts ().result ();
596+ compiledBytecodes += result .getBytecodeSize ();
597+ targetCodeSize += result .getTargetCodeSize ();
598+ targetCodeHash = targetCodeHash * 31 + Arrays .hashCode (result .getTargetCode ());
599+ }
600+
601+ public void endIteration (PrintStream out , PrintStream outStat ) {
602+ long endThreadTime = getCurrentThreadCpuTime ();
603+ long endWallTime = System .nanoTime ();
604+ long endMemory = getCurrentThreadAllocatedBytes ();
605+ long wallTimeNanos = endWallTime - beginWallTime ;
606+ long threadTimeNanos = endThreadTime - beginThreadTime ;
607+ long allocatedMemory = endMemory - beginMemory ;
608+ if (outStat != null ) {
609+ outStat .printf ("%d,%d,%d,%d,%d,%d,%08x%n" , iteration , wallTimeNanos , threadTimeNanos , allocatedMemory , compiledBytecodes , targetCodeSize , targetCodeHash );
610+ }
611+ out .printf (" Thread time: %12.3f ms%n" , threadTimeNanos / ONE_MILLION );
612+ out .printf (" Allocated memory: %12.3f MB%n" , allocatedMemory / ONE_MILLION );
613+ out .printf (" Compiled bytecodes: %12d B%n" , compiledBytecodes );
614+ out .printf (" Target code size: %12d B%n" , targetCodeSize );
615+ out .printf (" Target code hash: %08x%n" , targetCodeHash );
616+ out .printf ("====== replaycomp iteration %d completed (%.3f ms) ======%n" , iteration , wallTimeNanos / ONE_MILLION );
617+ }
618+ }
471619}
0 commit comments