2626import re
2727import os
2828from tempfile import mkstemp
29+ from typing import List , Optional
2930
3031import mx
3132import mx_benchmark
3233import mx_sdk_benchmark
3334import mx_compiler
3435from mx_java_benchmarks import DaCapoBenchmarkSuite , ScalaDaCapoBenchmarkSuite
36+ from mx_benchmark import DataPoints
37+ from mx_sdk_benchmark import SUCCESSFUL_STAGE_PATTERNS
3538
3639_suite = mx .suite ('compiler' )
3740
@@ -406,7 +409,45 @@ def benchSuiteName(self, bmSuiteArgs=None):
406409mx_benchmark .add_bm_suite (ScalaDaCapoTimingBenchmarkSuite ())
407410
408411
409- class JMHNativeImageBenchmarkMixin (mx_sdk_benchmark .NativeImageBenchmarkMixin ):
412+ class JMHNativeImageBenchmarkMixin (mx_benchmark .JMHBenchmarkSuiteBase , mx_sdk_benchmark .NativeImageBenchmarkMixin ):
413+
414+ def get_jmh_result_file (self , bm_suite_args : List [str ]) -> Optional [str ]:
415+ """
416+ Only generate a JMH result file in the run stage. Otherwise the file-based rule (see
417+ :class:`mx_benchmark.JMHJsonRule`) will produce datapoints at every stage, based on results from a previous
418+ stage.
419+ """
420+ if self .is_native_mode (bm_suite_args ) and not self .stages_info .fallback_mode :
421+ # At this point, the StagesInfo class may not have all the information yet, in that case we rely on the
422+ # requested stage. But if this function is called later again when it is fully set up, we have to use the
423+ # effective stage instead.
424+ # This is important so that the JMH parsing rule is only enabled when the stage actually ran (if it is
425+ # skipped, it would otherwise pick up a previous result file)
426+ if self .stages_info .is_set_up :
427+ current_stage = self .stages_info .effective_stage
428+ else :
429+ current_stage = self .stages_info .requested_stage
430+
431+ if current_stage not in ["agent" , "instrument-run" , "run" ]:
432+ return None
433+
434+ return super ().get_jmh_result_file (bm_suite_args )
435+
436+ def fallback_mode_reason (self , bm_suite_args : List [str ]) -> Optional [str ]:
437+ """
438+ JMH benchmarks need to use the fallback mode if --jmh-run-individually is used.
439+ The flag causes one native image to be built per JMH benchmark. This is fundamentally incompatible with the
440+ default benchmarking mode of running each stage on its own because a benchmark will overwrite the intermediate
441+ files of the previous benchmark if not all stages are run at once.
442+
443+ In the fallback mode, collection of performance data is limited. Only performance data of the ``run`` stage can
444+ reliably be collected. Other metrics, such as image build statistics or profiling performance cannot reliably be
445+ collected because they cannot be attributed so a specific individual JMH benchmark.
446+ """
447+ if self .jmhArgs (bm_suite_args ).jmh_run_individually :
448+ return "--jmh-run-individually is not compatible with selecting individual stages"
449+ else :
450+ return None
410451
411452 def extra_image_build_argument (self , benchmark , args ):
412453 # JMH does HotSpot-specific field offset checks in class initializers
@@ -462,6 +503,9 @@ def group(self):
462503 def subgroup (self ):
463504 return "graal-compiler"
464505
506+ def run (self , benchmarks , bmSuiteArgs ) -> DataPoints :
507+ return self .intercept_run (super (), benchmarks , bmSuiteArgs )
508+
465509
466510mx_benchmark .add_bm_suite (JMHRunnerGraalCoreBenchmarkSuite ())
467511
@@ -477,6 +521,9 @@ def group(self):
477521 def subgroup (self ):
478522 return "graal-compiler"
479523
524+ def run (self , benchmarks , bmSuiteArgs ) -> DataPoints :
525+ return self .intercept_run (super (), benchmarks , bmSuiteArgs )
526+
480527
481528mx_benchmark .add_bm_suite (JMHJarGraalCoreBenchmarkSuite ())
482529
@@ -492,10 +539,16 @@ def group(self):
492539 def subgroup (self ):
493540 return "graal-compiler"
494541
542+ def run (self , benchmarks , bmSuiteArgs ) -> DataPoints :
543+ return self .intercept_run (super (), benchmarks , bmSuiteArgs )
544+
495545 def filter_distribution (self , dist ):
496546 return super (JMHDistGraalCoreBenchmarkSuite , self ).filter_distribution (dist ) and \
497547 not JMHDistWhiteboxBenchmarkSuite .is_whitebox_dependency (dist )
498548
549+ def successPatterns (self ):
550+ return super ().successPatterns () + SUCCESSFUL_STAGE_PATTERNS
551+
499552
500553mx_benchmark .add_bm_suite (JMHDistGraalCoreBenchmarkSuite ())
501554
@@ -511,6 +564,9 @@ def group(self):
511564 def subgroup (self ):
512565 return "graal-compiler"
513566
567+ def run (self , benchmarks , bmSuiteArgs ) -> DataPoints :
568+ return self .intercept_run (super (), benchmarks , bmSuiteArgs )
569+
514570 @staticmethod
515571 def is_whitebox_dependency (dist ):
516572 return hasattr (dist , 'graalWhiteboxDistribution' ) and dist .graalWhiteboxDistribution
@@ -542,5 +598,8 @@ def getJMHEntry(self, bmSuiteArgs):
542598 assert self .dist
543599 return [mx .distribution (self .dist ).mainClass ]
544600
601+ def successPatterns (self ):
602+ return super ().successPatterns () + SUCCESSFUL_STAGE_PATTERNS
603+
545604
546605mx_benchmark .add_bm_suite (JMHDistWhiteboxBenchmarkSuite ())
0 commit comments