Skip to content

Commit e4c546a

Browse files
committed
Introduce --reuse-disk-images Polybench flag to support reusing images found on disk
1 parent eed5242 commit e4c546a

File tree

2 files changed

+71
-26
lines changed

2 files changed

+71
-26
lines changed

truffle/mx.truffle/mx_polybench/command.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,18 @@
4343
import shlex
4444
from argparse import ArgumentParser
4545
from enum import Enum
46-
from typing import List, Set, Tuple, NamedTuple
46+
from typing import List, Set, Tuple, NamedTuple, Dict
4747

4848
import mx
4949
import mx_benchmark
5050
import mx_sdk
51-
from mx_polybench.model import _resolve_all_benchmarks, _get_all_suites, PolybenchBenchmarkSuiteEntry
51+
from mx_polybench.model import (
52+
_resolve_all_benchmarks,
53+
_get_all_suites,
54+
PolybenchBenchmarkSuiteEntry,
55+
_extend_env,
56+
PolybenchBenchmarkSuite,
57+
)
5258

5359
_suite = mx.suite("truffle")
5460

@@ -200,6 +206,8 @@ def _run_suite(args):
200206
base_args.append("--dry-run")
201207
elif args.dry_run_polybench:
202208
base_args.append("--dry-run-polybench")
209+
elif args.reuse_disk_images:
210+
base_args.append("--reuse-disk-images")
203211

204212
def polybench_run_function(argument_list: List[str]) -> None:
205213
raw_args = base_args + argument_list + override_arguments
@@ -258,7 +266,9 @@ def _run_benchmark_pattern(args):
258266
run_spec = PolybenchRunSpecification(args.benchmarks, _get_vm_features(args), arguments_spec)
259267
_validate_jdk(run_spec.is_native())
260268
mx.logv(f"Performing polybench run: {run_spec}")
261-
_run_specification(run_spec, pattern_is_glob=args.pattern_is_glob, dry_run=args.dry_run)
269+
_run_specification(
270+
run_spec, pattern_is_glob=args.pattern_is_glob, dry_run=args.dry_run, reuse_disk_images=args.reuse_disk_images
271+
)
262272

263273

264274
def _validate_jdk(is_native: bool) -> mx.JDKConfig:
@@ -341,11 +351,6 @@ class PolybenchRunSpecification(NamedTuple):
341351
vm_features: Set[VMFeature] = set()
342352
arguments: PolybenchArgumentsSpecification = PolybenchArgumentsSpecification()
343353

344-
def append_arguments(self, other: PolybenchArgumentsSpecification) -> "PolybenchRunSpecification":
345-
return PolybenchRunSpecification(
346-
pattern=self.pattern, vm_features=self.vm_features, arguments=self.arguments.append(other)
347-
)
348-
349354
def is_native(self) -> bool:
350355
return VMFeature.NATIVE in self.vm_features
351356

@@ -361,7 +366,12 @@ def jvm_config(self) -> str:
361366
return "-".join(features) if features else "default"
362367

363368

364-
def _run_specification(spec: PolybenchRunSpecification, pattern_is_glob: bool = True, dry_run: bool = False):
369+
def _run_specification(
370+
spec: PolybenchRunSpecification,
371+
pattern_is_glob: bool = True,
372+
dry_run: bool = False,
373+
reuse_disk_images: bool = False,
374+
):
365375
pattern = _parse_mx_benchmark_pattern(spec.pattern, pattern_is_glob)
366376
mx_benchmark_args = (
367377
[f"polybench:{pattern}"]
@@ -377,7 +387,15 @@ def _run_specification(spec: PolybenchRunSpecification, pattern_is_glob: bool =
377387
return
378388

379389
mx.logv(f"Running command: {command_string}")
380-
mx_benchmark.benchmark(mx_benchmark_args)
390+
with _extend_env(_extra_run_variables(reuse_disk_images)):
391+
mx_benchmark.benchmark(mx_benchmark_args)
392+
393+
394+
def _extra_run_variables(reuse_disk_images) -> Dict[str, str]:
395+
result = {}
396+
if reuse_disk_images:
397+
result[PolybenchBenchmarkSuite.REUSE_DISK_IMAGES] = "true"
398+
return result
381399

382400

383401
def _base_mx_command() -> List[str]:
@@ -462,6 +480,16 @@ def run_flag(run_arg):
462480
parser.add_argument(
463481
run_flag("--g1gc"), action="store_true", default=False, help="use G1GC (only valid for native runs)"
464482
)
483+
parser.add_argument(
484+
run_flag("--reuse-disk-images"),
485+
action="store_true",
486+
default=False,
487+
help=(
488+
"reuse existing native images found on disk. Polybench will reuse an image from disk if it was built with the same languages and VM arguments. "
489+
"This feature does not detect stale images, does not support PGO, and should only be used for development. "
490+
f'This feature can also be enabled by setting the environment variable {PolybenchBenchmarkSuite.REUSE_DISK_IMAGES} to "true".'
491+
),
492+
)
465493
benchmark_pattern_group = parser.add_mutually_exclusive_group()
466494
benchmark_pattern_group.add_argument(
467495
run_flag("--suite"),

truffle/mx.truffle/mx_polybench/model.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ class PolybenchBenchmarkSuite(
284284
"application-memory-metric": "application-memory",
285285
"none": None,
286286
}
287+
REUSE_DISK_IMAGES = "POLYBENCH_REUSE_DISK_IMAGES"
287288

288289
def __init__(self):
289290
super(PolybenchBenchmarkSuite, self).__init__()
@@ -333,14 +334,14 @@ def run(self, benchmarks, bmSuiteArgs) -> DataPoints:
333334
working_directory = self.workingDirectory(benchmarks, bmSuiteArgs) or os.getcwd()
334335
resolved_benchmark = self._resolve_current_benchmark(benchmarks)
335336

336-
mx.log(f'Running polybench benchmark "{resolved_benchmark.name}"".')
337+
mx.log(f'Running polybench benchmark "{resolved_benchmark.name}".')
337338
mx.logv(f"CWD: {working_directory}")
338339
mx.logv(f"Languages included on the classpath: {resolved_benchmark.suite.languages}")
339340

340341
env_vars = PolybenchBenchmarkSuite._prepare_distributions(working_directory, resolved_benchmark)
341-
with _extend_env(env_vars):
342+
with _extend_env(env_vars), self._set_image_context(resolved_benchmark, bmSuiteArgs):
342343
if self._can_use_image_cache(bmSuiteArgs):
343-
return self._run_with_image_cache(resolved_benchmark, benchmarks, bmSuiteArgs)
344+
return self._run_with_image_cache(benchmarks, bmSuiteArgs)
344345
else:
345346
return self.intercept_run(super(), benchmarks, bmSuiteArgs)
346347

@@ -380,13 +381,10 @@ def _prepare_distributions(
380381
def _can_use_image_cache(self, bm_suite_args) -> bool:
381382
return self.is_native_mode(bm_suite_args) and "pgo" not in self.jvmConfig(bm_suite_args)
382383

383-
def _run_with_image_cache(
384-
self, resolved_benchmark: ResolvedPolybenchBenchmark, benchmarks: List[str], bm_suite_args: List[str]
385-
) -> DataPoints:
386-
with self._set_image_context(resolved_benchmark, bm_suite_args):
387-
image_build_datapoints = self._build_cached_image(benchmarks, bm_suite_args)
388-
image_run_datapoints = self._run_cached_image(benchmarks, bm_suite_args)
389-
return list(image_build_datapoints) + list(image_run_datapoints)
384+
def _run_with_image_cache(self, benchmarks: List[str], bm_suite_args: List[str]) -> DataPoints:
385+
image_build_datapoints = self._build_cached_image(benchmarks, bm_suite_args)
386+
image_run_datapoints = self._run_cached_image(benchmarks, bm_suite_args)
387+
return list(image_build_datapoints) + list(image_run_datapoints)
390388

391389
@contextlib.contextmanager
392390
def _set_image_context(self, resolved_benchmark: ResolvedPolybenchBenchmark, bm_suite_args: List[str]):
@@ -404,12 +402,11 @@ def _set_image_context(self, resolved_benchmark: ResolvedPolybenchBenchmark, bm_
404402

405403
def _base_image_name(self) -> Optional[str]:
406404
"""Overrides the image name used to build/run the image."""
407-
if self._current_image:
408-
return self._current_image.full_executable_name()
409-
return None
405+
assert self._current_image, "Image should have been set already"
406+
return self._current_image.full_executable_name()
410407

411408
def _build_cached_image(self, benchmarks: List[str], bm_suite_args: List[str]) -> DataPoints:
412-
if self._current_image in self._image_cache:
409+
if self._image_is_cached(bm_suite_args):
413410
# already built
414411
return []
415412

@@ -422,14 +419,33 @@ def _build_cached_image(self, benchmarks: List[str], bm_suite_args: List[str]) -
422419
datapoint["benchmark"] = self._current_image.executable_name()
423420
return image_build_datapoints
424421

422+
def _image_is_cached(self, bm_suite_args: List[str]) -> bool:
423+
if self._current_image in self._image_cache:
424+
return True
425+
426+
if mx.get_env(PolybenchBenchmarkSuite.REUSE_DISK_IMAGES) in ["true", "True"]:
427+
full_image_name = self.get_full_image_name(self.get_base_image_name(), self.jvmConfig(bm_suite_args))
428+
image_path = self.get_image_output_dir(
429+
self.benchmark_output_dir(self.benchmark_name, self.vmArgs(bm_suite_args)), full_image_name
430+
) / self.get_image_file_name(full_image_name)
431+
if os.path.exists(image_path):
432+
mx.warn(
433+
f"Existing image at {image_path} will be reused ({PolybenchBenchmarkSuite.REUSE_DISK_IMAGES} is set to true). "
434+
"Reusing disk images is a development feature and it does not detect stale images. Use with caution."
435+
)
436+
self._image_cache.add(self._current_image)
437+
return True
438+
439+
return False
440+
425441
def _run_cached_image(self, benchmarks: List[str], bm_suite_args: List[str]) -> DataPoints:
426442
return self.intercept_run(
427443
super(), benchmarks, self._extend_vm_args(bm_suite_args, ["-Dnative-image.benchmark.stages=run"])
428444
)
429445

430446
def _extend_vm_args(self, bm_suite_args: List[str], new_vm_args: List[str]) -> List[str]:
431-
vmArgs, runArgs = self.vmAndRunArgs(bm_suite_args)
432-
return vmArgs + new_vm_args + ["--"] + runArgs
447+
vm_args, run_args = self.vmAndRunArgs(bm_suite_args)
448+
return vm_args + new_vm_args + ["--"] + run_args
433449

434450
def createCommandLineArgs(self, benchmarks, bmSuiteArgs):
435451
resolved_benchmark = self._resolve_current_benchmark(benchmarks)
@@ -596,6 +612,7 @@ def parse(self, text) -> Iterable[DataPoint]:
596612

597613
@contextlib.contextmanager
598614
def _extend_env(env_vars: Dict[str, str]):
615+
"""Temporarily extends the environment variables for the extent of this context."""
599616
old_env = dict(os.environ)
600617
try:
601618
for k, v in env_vars.items():

0 commit comments

Comments
 (0)