@@ -340,7 +340,7 @@ def get_bundle_path_for_benchmark_layer(self, benchmark, layer_info) -> str:
340
340
return str (nib_candidates [0 ])
341
341
342
342
def get_latest_layer (self ) -> Optional [Layer ]:
343
- latest_image_stage = self .execution_context . virtual_machine . stages_info .get_latest_image_stage ()
343
+ latest_image_stage = self .stages_info .get_latest_image_stage ()
344
344
if latest_image_stage is None or not latest_image_stage .is_layered ():
345
345
return None
346
346
return latest_image_stage .layer_info
@@ -391,9 +391,9 @@ def ensure_image_is_at_desired_location(self, bmSuiteArgs):
391
391
# we need to move the image from the path that is set inside the nib to the path expected by our vm.
392
392
# This code has no effect if the image is already at the desired location.
393
393
vm = self .get_vm_registry ().get_vm_from_suite_args (bmSuiteArgs )
394
- if vm .stages_info .should_produce_datapoints (StageName .INSTRUMENT_IMAGE ):
394
+ if self .stages_info .should_produce_datapoints (StageName .INSTRUMENT_IMAGE ):
395
395
desired_image_path = vm .config .instrumented_image_path
396
- elif vm .stages_info .should_produce_datapoints (StageName .IMAGE ):
396
+ elif self .stages_info .should_produce_datapoints (StageName .IMAGE ):
397
397
desired_image_path = vm .config .image_path
398
398
else :
399
399
return
@@ -515,53 +515,55 @@ def produceHarnessCommand(self, cmd, suite):
515
515
# and the scenario files (GR-65000)
516
516
_graalosConfig = {
517
517
"benchmarks" : {
518
- "local_binary" : {
519
- "app" : "rawhttp-function" ,
520
- "scenario-path" : "local_binary.toml" ,
521
- },
522
- "dataplane_smoke_test" : {
523
- "app" : "rawhttp-function" ,
524
- "scenario-path" : "dataplane_smoke_test.toml" ,
525
- },
526
- "graal-ci-round-robin-rawhttp-function-256" : {
527
- "app" : "rawhttp-function" ,
528
- "scenario-path" : "graal-ci/round-robin/rawhttp-function-256.toml" ,
529
- },
530
- "graal-ci-single-app-micronaut-pegasus-function-256" : {
518
+ "round-robin-micronaut-pegasus-function" : {
531
519
"app" : "micronaut-pegasus-function" ,
532
- "scenario-path" : "graal-ci/single-app /micronaut-pegasus-function-256 .toml" ,
520
+ "scenario-path" : "graalos-bench-suite/round-robin /micronaut-pegasus-function.toml" ,
533
521
},
534
- "graal-ci-single-app-micronaut-pegasus-function-2048" : {
535
- "app" : "micronaut-pegasus-function" ,
536
- "scenario-path" : "graal-ci/single-app/micronaut-pegasus-function-2048.toml" ,
537
- },
538
- "graal-ci-single-app-rawhttp-function-256" : {
522
+ "round-robin-rawhttp-function" : {
539
523
"app" : "rawhttp-function" ,
540
- "scenario-path" : "graal-ci/single-app /rawhttp-function-256 .toml" ,
524
+ "scenario-path" : "graalos-bench-suite/round-robin /rawhttp-function.toml" ,
541
525
},
542
- "graal-ci- single-app-rawhttp-function-2048 " : {
543
- "app" : "rawhttp -function" ,
544
- "scenario-path" : "graal-ci /single-app/rawhttp-function-2048 .toml" ,
526
+ "single-app-micronaut-pegasus-function " : {
527
+ "app" : "micronaut-pegasus -function" ,
528
+ "scenario-path" : "graalos-bench-suite /single-app/micronaut-pegasus-function .toml" ,
545
529
},
546
- "graal-ci-smoke-test- rawhttp-function-256 " : {
530
+ "single-app- rawhttp-function" : {
547
531
"app" : "rawhttp-function" ,
548
- "scenario-path" : "graal-ci/smoke-test/rawhttp-function-256.toml" ,
549
- },
550
- "graal-ci-stress-test-timed-compute-function-256" : {
551
- "app" : "timed-compute-function" ,
552
- "scenario-path" : "graal-ci/stress-test/timed-compute-function-256.toml" ,
532
+ "scenario-path" : "graalos-bench-suite/single-app/rawhttp-function.toml" ,
553
533
},
554
534
},
555
535
}
556
536
557
537
class GraalOSNativeImageBenchmarkSuite (mx_benchmark .CustomHarnessBenchmarkSuite ,
558
538
mx_sdk_benchmark .NativeImageBenchmarkMixin ,
559
- mx_sdk_benchmark .NativeImageBundleBasedBenchmarkMixin ):
539
+ mx_sdk_benchmark .LayeredNativeImageBundleBasedBenchmarkMixin ):
560
540
"""
561
541
A collection of benchmarks designed for benchmarking the performance of apps built for GraalOS.
562
542
563
543
This benchmark suite utilizes the `graalos-load-tester` harness to execute scenarios that run workloads against
564
544
images of apps located in the `vm-benchmarks/graalos` repository.
545
+
546
+ The run arguments (arguments that are specified after the second '--' instance of the 'mx benchmark' command) are
547
+ passed to the 'gos-scenario' tool. For example:
548
+ ```
549
+ mx ... benchmark graalos:... -- ... -- -p app_count=1
550
+ ```
551
+ will propagate '-p app_count=1' to the 'gos-scenario' harness and thus set the 'app_count' parameter of the
552
+ selected scenario to 1.
553
+
554
+ DEVELOPER NOTE:
555
+ The automatic dependency setup implemented as part of this suite does not work when multiple 'mx benchmark'
556
+ processes are running concurrently. Namely, the bench suite automatically executes the following steps, if they
557
+ haven't already been executed:
558
+ * cloning the 'graalos-load-tester' repository as a sibling to the 'graal' repository
559
+ * downloading the tools which graalos-load-tester depends on (e.g. wrk, wrk2, nginx)
560
+ * generating the native image bundle from which the application image is built
561
+ These steps aren't executed if:
562
+ * the 'graalos-load-tester' repository is already present as a sibling to the 'graal' repository
563
+ * all of the tools which graalos-load-tester depends on are installed and available on the PATH
564
+ * the native image bundle for the application is present in the application source code directory
565
+ Only in the case that these requirements are satisfied running concurrent 'mx benchmark' processes will not crash.
566
+ (GR-66385)
565
567
"""
566
568
def __init__ (self , custom_harness_command : mx_benchmark .CustomHarnessCommand = None ):
567
569
if custom_harness_command is None :
@@ -591,6 +593,19 @@ def version(self):
591
593
mx .log (f"Running GraalOS Load Tester version '{ self ._version } '" )
592
594
return self ._version
593
595
596
+ def layers (self , bm_suite_args : List [str ]) -> List [Layer ]:
597
+ if self ._get_benchmark_config (self .benchmarkName ())["app" ] == "micronaut-pegasus-function" :
598
+ return [Layer (0 , True ), Layer (1 , False )]
599
+ # Currently, "micronaut-pegasus-function" is the only app that supports running with layers
600
+ # Support for other benchmarks, or even suites? (GR-64772)
601
+ mx .abort (f"The '{ self .benchmarkName ()} ' benchmark does not support layered native images!" )
602
+
603
+ def get_latest_layer (self ) -> Optional [Layer ]:
604
+ latest_image_stage = self .stages_info .get_latest_image_stage ()
605
+ if latest_image_stage is None or not latest_image_stage .is_layered ():
606
+ return None
607
+ return latest_image_stage .layer_info
608
+
594
609
@property
595
610
def gos_scenario_home (self ) -> Path :
596
611
if self ._gos_scenario_home is None :
@@ -730,45 +745,77 @@ def _get_benchmark_config(self, benchmark):
730
745
"""
731
746
return _graalosConfig ["benchmarks" ][benchmark ]
732
747
733
- def applicationDist (self ):
748
+ def get_bundle_path (self ) -> str :
734
749
app_name = self ._get_benchmark_config (self .benchmarkName ())["app" ]
735
- if app_name not in self ._bundle_paths :
736
- self ._bundle_paths [app_name ] = self .generate_or_lookup_bundle ()
737
- mx .log (f"Using bundle at '{ self ._bundle_paths [app_name ]} ' for app '{ app_name } '." )
738
- return self ._bundle_paths [app_name ].parent
750
+ layer_info = self .get_latest_layer ()
751
+ if layer_info is not None :
752
+ key = f"{ app_name } -layer{ layer_info .index } "
753
+ else :
754
+ key = app_name
739
755
740
- def generate_or_lookup_bundle (self ) -> Path :
756
+ if key not in self ._bundle_paths :
757
+ app_dir = self ._app_source_dir (app_name )
758
+ self ._bundle_paths [key ] = self .generate_or_lookup_bundle (app_name , layer_info , app_dir )
759
+ mx .log (f"Using bundle at '{ self ._bundle_paths [key ]} ' to generate the image for '{ key } '." )
760
+ return self ._bundle_paths [key ]
761
+
762
+ def lookup_bundle (self , app_name : str , layer_info : Layer , app_dir : Path ) -> List [Path ]:
763
+ """
764
+ Looks up the path to the NIB file for the app-layer pair associated with the current benchmark stage.
765
+
766
+ The files are searched for in the subtree of the app's root directory. All the files that match the expected
767
+ naming pattern are matched. The naming pattern depends on whether the bundle has been generated for a standalone
768
+ application or a single layer of a layered application build. The name of the NIB file should:
769
+ * start with anything other than 'layer<NUMBER>-' if it is meant for building a standalone app image.
770
+ * start with 'layer<NUMBER>-' if it is meant for building a layer, where NUMBER is the index of the layer.
771
+ """
772
+ # Lookup all the NIB files located inside the subtree of the app root directory
773
+ nib_candidates = list (app_dir .glob ("**/*.nib" ))
774
+
775
+ # Filter for only the NIB files that correspond to the naming scheme associated with the current layer
776
+ if layer_info is None :
777
+ # Select only the nib files that do not start with r'layer\d+-'
778
+ nib_naming_pattern = r"^(?!layer\d+-).*\.nib$"
779
+ else :
780
+ # Select only the nib files that start with fr'layer{layer_info.index}-'
781
+ nib_naming_pattern = fr"^layer{ layer_info .index } -.*\.nib$"
782
+ return [nib for nib in nib_candidates if re .match (nib_naming_pattern , nib .name )]
783
+
784
+ def generate_bundle (self , app_name : str , layer_info : Layer ):
785
+ """Generates the NIB file for the app-layer pair associated with the current benchmark stage."""
786
+ if app_name == "micronaut-pegasus-function" and mx .get_env ("JDK17_HOME" ) is None :
787
+ mx .abort (f"App '{ app_name } ' requires JDK17_HOME env var to point to a JDK 17 distribution in order to build maven project." )
788
+
789
+ nib_generation_cmd = ["./graalos-gate.py" , "build" , "--build-profile" , "nib" , app_name ]
790
+ if layer_info is not None :
791
+ assert app_name == "micronaut-pegasus-function" , f"Cannot generate a layer bundle for '{ app_name } ' app!"
792
+ assert layer_info .index in [0 , 1 ], f"Cannot generate layer#{ layer_info .index } bundle for '{ app_name } ' app!"
793
+ if layer_info .index == 0 :
794
+ nib_generation_cmd += ["--extra-build-options=--maven-options=-Pbase-layer" ]
795
+ else :
796
+ nib_generation_cmd += ["--extra-build-options=--maven-options=-Papp-layer" ]
797
+ working_dir = self ._vm_benchmarks_graalos_dir ()
798
+ mx .log (f"Generating the NIB file by running { nib_generation_cmd } in working dir '{ working_dir } '. This can take a while." )
799
+ try :
800
+ mx .run (nib_generation_cmd , cwd = working_dir , env = self .get_nib_generation_env ())
801
+ except BaseException as e :
802
+ if isinstance (e , SystemExit ):
803
+ mx .abort (f"Generating the NIB file failed with exit code { e } !" )
804
+ else :
805
+ mx .abort (f"{ e } \n Generating the NIB file failed!" )
806
+
807
+ def generate_or_lookup_bundle (self , app_name : str , layer_info : Layer , app_dir : Path ) -> Path :
741
808
"""
742
809
Looks up the path to the NIB file for the app asociated with the current benchmark,
743
810
generating it first if it does not exist.
744
811
"""
745
- # Initial NIB lookup
746
- app_name = self ._get_benchmark_config (self .benchmarkName ())["app" ]
747
- app_dir = self ._app_source_dir (app_name )
748
- nib_candidates = list (app_dir .glob ("**/*.nib" ))
812
+ nib_candidates = self .lookup_bundle (app_name , layer_info , app_dir )
749
813
750
814
# Generate a NIB file for the app if none exists
751
815
if len (nib_candidates ) == 0 :
752
- nib_generation_cmd = ["./graalos-gate.py" , "build" , "--build-profile" , "nib" , app_name ]
753
- working_dir = self ._vm_benchmarks_graalos_dir ()
754
- mx .log (f"Generating the NIB file by running { nib_generation_cmd } in working dir { working_dir } " )
755
- out = mx .OutputCapture ()
756
- err = mx .OutputCapture ()
757
- try :
758
- mx .run (nib_generation_cmd , cwd = working_dir , out = out , err = err , env = self .get_nib_generation_env ())
759
- except BaseException as e :
760
- for line in out .data .split ("\n " ):
761
- mx .log (line )
762
- for line in err .data .split ("\n " ):
763
- mx .log_error (line )
764
- if isinstance (e , SystemExit ):
765
- mx .abort (f"Generating the NIB file failed with exit code { e } !" )
766
- else :
767
- mx .abort (f"{ e } \n Generating the NIB file failed!" )
768
- for line in out .data .split ("\n " ):
769
- mx .logvv (line )
816
+ self .generate_bundle (app_name , layer_info )
770
817
# Repeat the lookup
771
- nib_candidates = list ( app_dir . glob ( "**/*.nib" ) )
818
+ nib_candidates = self . lookup_bundle ( app_name , layer_info , app_dir )
772
819
773
820
# Final check
774
821
if len (nib_candidates ) == 0 :
@@ -801,6 +848,12 @@ def extra_run_arg(self, benchmark, args, image_run_args):
801
848
# Added by GraalOSLoadTesterCommand
802
849
return []
803
850
851
+ def build_assertions (self , benchmark : str , is_gate : bool ) -> List [str ]:
852
+ # We cannot enable assertions along with emitting a build report for layered images, due to GR-65751
853
+ if self .stages_info .current_stage .is_layered :
854
+ return []
855
+ return super ().build_assertions (benchmark , is_gate )
856
+
804
857
def rules (self , output , benchmarks , bmSuiteArgs ) -> List [Rule ]:
805
858
json_file_group_name = "graalos_json_results_file_path"
806
859
json_file_pattern = fr"- saved to: (?P<{ json_file_group_name } >\S+?)$"
@@ -891,8 +944,13 @@ def get_stage_env(self) -> Optional[dict]:
891
944
def get_nib_generation_env (self ):
892
945
env = self .get_stage_env ().copy ()
893
946
graalvm_home = self .execution_context .virtual_machine .home ()
894
- env ["GRAALVM_HOME" ] = graalvm_home
895
- env ["GRADLE_CLIENT_JAVA_HOME" ] = graalvm_home
947
+ # graalos-gate.py (the script we use to generate the NIB files) requires these env vars
948
+ # - GRAALVM_HOME so the underlying maven/gradle/custom build command can invoke native-image
949
+ # - GRADLE_CLIENT_JAVA_HOME so that gradle has a JVM to execute its goals
950
+ if not env .get ("GRAALVM_HOME" ):
951
+ env ["GRAALVM_HOME" ] = graalvm_home
952
+ if not env .get ("GRADLE_CLIENT_JAVA_HOME" ):
953
+ env ["GRADLE_CLIENT_JAVA_HOME" ] = graalvm_home
896
954
return env
897
955
898
956
def run (self , benchmarks , bmSuiteArgs ) -> DataPoints :
@@ -919,7 +977,6 @@ def produceHarnessCommand(self, cmd: List[str], suite: BenchmarkSuite) -> List[s
919
977
app_cmd = source_cmd_prefix
920
978
app_cmd += [str (suite ._get_runnable_app_image ())]
921
979
app_cmd += options_from_source_cmd
922
- app_cmd += suite .runArgs (bmSuiteArgs )
923
980
app_cmd += parse_prefixed_args ("-Dnative-image.benchmark.extra-jvm-arg=" , bmSuiteArgs )
924
981
if suite .stages_info .current_stage .is_instrument ():
925
982
# Add explicit instrument stage args
@@ -933,6 +990,7 @@ def produceHarnessCommand(self, cmd: List[str], suite: BenchmarkSuite) -> List[s
933
990
timestamp = datetime .datetime .now ().strftime ("%Y-%m-%d-%H-%M-%S" )
934
991
gos_log_file_name = f"{ timestamp } -gos-out.log"
935
992
gos_cmd += ["--log-to" , f"stdout,file:{ gos_log_file_name } " ]
993
+ gos_cmd += suite .runArgs (bmSuiteArgs )
936
994
app_cmd_str = " " .join (app_cmd )
937
995
gos_cmd += ["-p" , f"command=\" { app_cmd_str } \" " ]
938
996
mx .log (f"Produced 'gos-scenario' command: '{ ' ' .join (gos_cmd )} '" )
0 commit comments