Skip to content

Commit 08941b0

Browse files
author
Andrija Kolic
committed
Add 'pyodide' benchmark vm.
1 parent 6f40847 commit 08941b0

File tree

2 files changed

+135
-4
lines changed

2 files changed

+135
-4
lines changed

sdk/mx.sdk/mx_sdk_benchmark.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,15 @@ def fullname(cls):
798798

799799
self.stages_info = self.bmSuite.stages_info
800800
assert not self.stages_info.failed, "In case of a failed benchmark, no further calls into the VM should be made"
801-
assert self.stages_info.vm_used_for_stages == self, f"VM used to prepare stages ({self.stages_info.vm_used_for_stages}) cannot be different from the VM used to run the suite ({self})!"
801+
vm_used_for_stages = self.stages_info.vm_used_for_stages
802+
same_vm = vm_used_for_stages == self
803+
host_and_guest_used = (
804+
isinstance(vm_used_for_stages, mx_benchmark.GuestVm)
805+
and vm_used_for_stages.host_vm() == self
806+
)
807+
assert same_vm or host_and_guest_used, (
808+
f"VM used to prepare stages ({vm_used_for_stages}) cannot be different from the VM used to run the suite ({self})!"
809+
)
802810

803811
def get_required_benchmark_suite_mixin_class(self):
804812
return StageAwareBenchmarkMixin
@@ -2062,6 +2070,120 @@ def get_stage_runner(self) -> SimpleStageRunner:
20622070
return SimpleStageRunner(self.stages_info, self.bmSuite, self.stages_context)
20632071

20642072

2073+
class PolyBenchEmscriptenHostVm(PolyBenchStagingVm):
2074+
"""Host VM that implements Emscripten used for running staged PolyBench programs through a guest-provided launcher."""
2075+
def run_stage_run(self):
2076+
vm = bm_exec_context().get("vm")
2077+
if not isinstance(vm, PolyBenchEmscriptenGuestVm):
2078+
mx.abort(f"{self.__class__.__name__} expects a PolyBenchEmscriptenGuestVm guest VM in the execution context.")
2079+
if vm.host_vm() != self:
2080+
mx.abort(f"Guest VM '{vm.name()}:{vm.config_name()}' is not hosted by '{self.name()}:{self.config_name()}'.")
2081+
2082+
guest_launcher = vm.guest_launcher(self)
2083+
if not Path(guest_launcher).is_file():
2084+
raise ValueError(f"Guest launcher '{guest_launcher}' does not resolve to a file!")
2085+
with self.get_stage_runner() as s:
2086+
cmd = [self.launcher, guest_launcher, str(self.staged_program_file_path)]
2087+
s.execute_command(self, cmd)
2088+
2089+
2090+
class PolyBenchEmscriptenGuestVm(mx_benchmark.GuestVm):
2091+
"""Abstract PolyBench Guest VM entry for running on an Emscripten host VM."""
2092+
def __init__(self, host_vm=None):
2093+
super().__init__(host_vm)
2094+
self._guest_run_on_java_home_value = None
2095+
self._guest_run_on_java_home_set = False
2096+
2097+
def guest_launcher(self, host_vm: PolyBenchEmscriptenHostVm) -> str:
2098+
"""
2099+
Return the launcher executable that the Emscripten host should invoke for this guest.
2100+
The returned path must resolve to a file and is used as the first command element.
2101+
"""
2102+
raise NotImplementedError()
2103+
2104+
def set_run_on_java_home(self, value):
2105+
self._guest_run_on_java_home_value = value
2106+
self._guest_run_on_java_home_set = True
2107+
host = self.host_vm()
2108+
if host is not None and hasattr(host, "set_run_on_java_home"):
2109+
host.set_run_on_java_home(value)
2110+
2111+
def run_on_java_home(self):
2112+
if self._guest_run_on_java_home_set:
2113+
return self._guest_run_on_java_home_value
2114+
host = self.host_vm()
2115+
if host is not None and hasattr(host, "run_on_java_home"):
2116+
return host.run_on_java_home()
2117+
return None
2118+
2119+
2120+
class PolyBenchPyodideGuestVm(PolyBenchEmscriptenGuestVm):
2121+
"""
2122+
Guest VM entry for Pyodide running on an Emscripten host VM.
2123+
Relies on the PYODIDE_BOOTSTRAP env var to point to the Pyodide bootstrap module.
2124+
"""
2125+
HOSTED_INSTANCE = "PolyBenchPyodideGuestVm.hosted="
2126+
BOOTSTRAP_LAUNCHER = "$PYODIDE_BOOTSTRAP"
2127+
2128+
def __init__(self, config_name, extra_java_args=None, extra_launcher_args=None, host_vm=None):
2129+
super().__init__(host_vm)
2130+
self._config_name = self.canonical_config_name(config_name)
2131+
2132+
def name(self):
2133+
return "pyodide"
2134+
2135+
def config_name(self):
2136+
return self._config_name
2137+
2138+
def with_host_vm(self, host_vm):
2139+
hosted_name = f"{self.HOSTED_INSTANCE}{self.name()}:{self.config_name()}@{host_vm.name()}:{host_vm.config_name()}"
2140+
if not bm_exec_context().has(hosted_name):
2141+
# Ensure the selected host VM is actually attached to the guest VM instance
2142+
hosted_instance = self.__class__(self.config_name(), host_vm=host_vm)
2143+
bm_exec_context().add_context_value(hosted_name, ConstantContextValue(hosted_instance))
2144+
hosted_instance = bm_exec_context().get(hosted_name)
2145+
if self._guest_run_on_java_home_set:
2146+
hosted_instance.set_run_on_java_home(self._guest_run_on_java_home_value)
2147+
return hosted_instance
2148+
2149+
@staticmethod
2150+
def canonical_config_name(config_name):
2151+
return config_name if config_name else "default"
2152+
2153+
def hosting_registry(self):
2154+
return mx_benchmark.java_vm_registry
2155+
2156+
def guest_launcher(self, host_vm: PolyBenchEmscriptenHostVm) -> str:
2157+
"""
2158+
Return the custom bootstrap script that starts a JavaScript module inside the
2159+
selected Emscripten host and forwards the staged Python benchmark into Pyodide.
2160+
Pyodide is embedded in that Emscripten environment rather than exposed as a
2161+
standalone terminal launcher, so the benchmark needs this adapter entry point.
2162+
"""
2163+
return PolyBenchStagingVm._resolve_possible_env_var(self.BOOTSTRAP_LAUNCHER)
2164+
2165+
def _host_vm_for_execution(self) -> PolyBenchEmscriptenHostVm:
2166+
host = self.host_vm()
2167+
if host is None:
2168+
mx.abort("Pyodide guest VM requires a host VM; none was provided.")
2169+
if not isinstance(host, PolyBenchEmscriptenHostVm):
2170+
mx.abort(f"Pyodide guest VM requires PolyBenchEmscriptenHostVm host; got {host.__class__.__name__}.")
2171+
return host
2172+
2173+
def run(self, cwd, args):
2174+
host = self._host_vm_for_execution()
2175+
return host.runWithSuite(self.bmSuite, cwd, args)
2176+
2177+
def prepare_stages(self, bm_suite: NativeImageBenchmarkMixin, bm_suite_args):
2178+
host = self._host_vm_for_execution()
2179+
effective_stages, complete_stage_list = host.prepare_stages(bm_suite, bm_suite_args)
2180+
return effective_stages, complete_stage_list
2181+
2182+
def runWithSuite(self, bmSuite, cwd, args):
2183+
host = self._host_vm_for_execution()
2184+
return host.runWithSuite(bmSuite, cwd, args)
2185+
2186+
20652187
class GraalHostPolyBenchStagingVm(PolyBenchStagingVm):
20662188
"""
20672189
Stages a PolyBench benchmark and configures a GraalHost boot script that runs the benchmark.
@@ -2207,6 +2329,8 @@ def register_graalvm_vms():
22072329
mx_benchmark.add_java_vm(PolyBenchStagingVm('cpython', 'default', "Python", "python", "py"), _suite, 2)
22082330
mx_benchmark.add_java_vm(PolyBenchStagingVm('cpython', 'graalos-ee', "Python", "$GRAALOS_CPYTHON", "py"), _suite, 2)
22092331
mx_benchmark.add_java_vm(GraalHostPolyBenchStagingVm('cpython', 'graalhost-graalos-ee', "Python", "$GRAALOS_CPYTHON", "py"), _suite, 2)
2332+
mx_benchmark.add_java_vm(PolyBenchEmscriptenHostVm('nodejs', 'default', "Python", "node", "py"), _suite, 2)
2333+
mx_benchmark.add_java_vm(PolyBenchPyodideGuestVm('default'), _suite, 2)
22102334

22112335

22122336
class ObjdumpSectionRule(mx_benchmark.StdOutRule):

truffle/mx.truffle/mx_polybench/model.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,9 +1376,7 @@ def run(self, benchmarks, bmSuiteArgs) -> DataPoints:
13761376
return datapoints
13771377

13781378
def use_stage_aware_benchmark_mixin_intercept_run(self):
1379-
if self.jvm(bm_exec_context().get("bm_suite_args")) == "cpython":
1380-
return True
1381-
return False
1379+
return self.jvm(bm_exec_context().get("bm_suite_args")) in ["cpython", "pyodide"]
13821380

13831381
def _resolve_current_benchmark(self, benchmarks) -> ResolvedPolybenchBenchmark:
13841382
if benchmarks is None or len(benchmarks) != 1:
@@ -1511,6 +1509,10 @@ def runAndReturnStdOut(self, benchmarks, bmSuiteArgs):
15111509
return ret_code, out, dims
15121510

15131511
def _infer_host_vm_config(self, bm_suite_args, dims):
1512+
selected_vm = self.get_vm_registry().get_vm_from_suite_args(bm_suite_args)
1513+
if isinstance(selected_vm, mx_benchmark.GuestVm):
1514+
return selected_vm.host_vm().config_name()
1515+
15141516
edition = dims.get("platform.graalvm-edition", "unknown").lower()
15151517
if edition not in ["ce", "ee"] or not dims.get("platform.prebuilt-vm", False):
15161518
raise ValueError(f"Polybench should only run with a prebuilt GraalVM. Dimensions found: {dims}")
@@ -1533,6 +1535,11 @@ def _infer_host_vm_config(self, bm_suite_args, dims):
15331535
return "graal-enterprise-libgraal-pgo" if edition == "ee" else "graal-core-libgraal"
15341536

15351537
def _infer_guest_vm_info(self, benchmarks, bm_suite_args) -> Tuple[str, str]:
1538+
# Prefer an explicit GuestVm if selected via the VmRegistry.
1539+
vm = self.get_vm_registry().get_vm_from_suite_args(bm_suite_args)
1540+
if isinstance(vm, mx_benchmark.GuestVm):
1541+
return (vm.name(), vm.config_name())
1542+
15361543
resolved_benchmark = self._resolve_current_benchmark(benchmarks)
15371544
# Eventually this must check for exact match for each language and map it to the corresponding guest-vm.
15381545
# Here, we just infer it based on the presence of some language in a list. This must be made more robust

0 commit comments

Comments
 (0)